= {
- expressions: npSetup.plugins.expressions,
- data: npSetup.plugins.data,
- visualizations: npSetup.plugins.visualizations,
-};
-
-const pluginInstance = plugin({} as PluginInitializerContext);
-
-export const setup = pluginInstance.setup(npSetup.core, setupPlugins);
-export const start = pluginInstance.start(npStart.core, npStart.plugins);
diff --git a/src/legacy/server/logging/log_reporter.js b/src/legacy/server/logging/log_reporter.js
index 6e62a5ee284e3..b784d03a5b86e 100644
--- a/src/legacy/server/logging/log_reporter.js
+++ b/src/legacy/server/logging/log_reporter.js
@@ -30,7 +30,7 @@ import { LogInterceptor } from './log_interceptor';
// thrown every time we start the server.
// In order to keep using the legacy logger until we remove it I'm just adding
// a new hard limit here.
-process.stdout.setMaxListeners(15);
+process.stdout.setMaxListeners(25);
export function getLoggerStream({ events, config }) {
const squeeze = new Squeeze(events);
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 f577a29ce90b9..f14f26613ef01 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
@@ -242,6 +242,7 @@ export const npSetup = {
},
kibanaLegacy: {
registerLegacyApp: () => {},
+ registerLegacyAppAlias: () => {},
forwardApp: () => {},
config: {
defaultAppId: 'home',
@@ -362,6 +363,7 @@ export const npStart = {
kibanaLegacy: {
getApps: () => [],
getForwards: () => [],
+ getLegacyAppAliases: () => [],
config: {
defaultAppId: 'home',
},
diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts
index 80fb837258d4c..5ae2e2348aaa1 100644
--- a/src/legacy/ui/public/new_platform/new_platform.ts
+++ b/src/legacy/ui/public/new_platform/new_platform.ts
@@ -69,6 +69,7 @@ import {
VisualizationsSetup,
VisualizationsStart,
} from '../../../../plugins/visualizations/public';
+import { VisTypeTimelionPluginStart } from '../../../../plugins/vis_type_timelion/public';
import { MapsLegacyPluginSetup } from '../../../../plugins/maps_legacy/public';
export interface PluginsSetup {
@@ -116,6 +117,7 @@ export interface PluginsStart {
telemetry?: TelemetryPluginStart;
dashboard: DashboardStart;
savedObjectsManagement: SavedObjectsManagementPluginStart;
+ visTypeTimelion: VisTypeTimelionPluginStart;
indexPatternManagement: IndexPatternManagementStart;
}
diff --git a/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts b/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts
new file mode 100644
index 0000000000000..b226bad11a01a
--- /dev/null
+++ b/src/plugins/console/server/__tests__/proxy_route/proxy_fallback.test.ts
@@ -0,0 +1,64 @@
+/*
+ * 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 { duration } from 'moment';
+import { getProxyRouteHandlerDeps } from './mocks';
+
+import { kibanaResponseFactory } from '../../../../../core/server';
+import { createHandler } from '../../routes/api/console/proxy/create_handler';
+import * as requestModule from '../../lib/proxy_request';
+
+describe('Console Proxy Route', () => {
+ afterEach(async () => {
+ jest.resetAllMocks();
+ });
+
+ describe('fallback behaviour', () => {
+ it('falls back to all configured endpoints regardless of error', async () => {
+ // Describe a situation where all three configured nodes reject
+ (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('ECONNREFUSED'));
+ (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('EHOSTUNREACH'));
+ (requestModule.proxyRequest as jest.Mock).mockRejectedValueOnce(new Error('ESOCKETTIMEDOUT'));
+
+ const handler = createHandler(
+ getProxyRouteHandlerDeps({
+ readLegacyESConfig: () => ({
+ requestTimeout: duration(30000),
+ customHeaders: {},
+ requestHeadersWhitelist: [],
+ hosts: ['http://localhost:9201', 'http://localhost:9202', 'http://localhost:9203'],
+ }),
+ })
+ );
+
+ const response = await handler(
+ {} as any,
+ {
+ headers: {},
+ query: { method: 'get', path: 'test' },
+ } as any,
+ kibanaResponseFactory
+ );
+
+ expect(response.status).toBe(502);
+ // Return the message from the ES node we attempted last.
+ expect(response.payload.message).toBe('ESOCKETTIMEDOUT');
+ });
+ });
+});
diff --git a/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json
index 408b01c4cb8f5..0da2c130b47cf 100644
--- a/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json
+++ b/src/plugins/console/server/lib/spec_definitions/json/overrides/cluster.put_settings.json
@@ -59,7 +59,61 @@
}
},
"transient": {
- "__scope_link": ".persistent"
+ "cluster": {
+ "routing": {
+ "allocation.enable": {
+ "__one_of": ["all", "primaries", "new_primaries", "none"]
+ },
+ "allocation.disk.threshold_enabled": { "__one_of": [false, true] },
+ "allocation.disk.watermark.low": "85%",
+ "allocation.disk.watermark.high": "90%",
+ "allocation.disk.reroute_interval": "60s",
+ "allocation.exclude": {
+ "_ip": "",
+ "_name": "",
+ "_host": "",
+ "_id": ""
+ },
+ "allocation.include": {
+ "_ip": "",
+ "_name": "",
+ "_host": "",
+ "_id": ""
+ },
+ "allocation.require": {
+ "_ip": "",
+ "_name": "",
+ "_host": "",
+ "_id": ""
+ },
+ "allocation.awareness.attributes": [],
+ "allocation.awareness.force": {
+ "*": {
+ "values": []
+ }
+ },
+ "allocation.allow_rebalance": {
+ "__one_of": [
+ "always",
+ "indices_primaries_active",
+ "indices_all_active"
+ ]
+ },
+ "allocation.cluster_concurrent_rebalance": 2,
+ "allocation.node_initial_primaries_recoveries": 4,
+ "allocation.node_concurrent_recoveries": 2,
+ "allocation.same_shard.host": { "__one_of": [false, true] }
+ }
+ },
+ "indices": {
+ "breaker": {
+ "total.limit": "70%",
+ "fielddata.limit": "60%",
+ "fielddata.overhead": 1.03,
+ "request.limit": "40%",
+ "request.overhead": 1.0
+ }
+ }
}
}
}
diff --git a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts
index 50a9fcf03c209..9446289ff03ea 100644
--- a/src/plugins/console/server/routes/api/console/proxy/create_handler.ts
+++ b/src/plugins/console/server/routes/api/console/proxy/create_handler.ts
@@ -175,10 +175,9 @@ export const createHandler = ({
break;
} catch (e) {
+ // If we reached here it means we hit a lower level network issue than just, for e.g., a 500.
+ // We try contacting another node in that case.
log.error(e);
- if (e.code !== 'ECONNREFUSED') {
- return response.internalError(e);
- }
if (idx === hosts.length - 1) {
log.warn(`Could not connect to any configured ES node [${hosts.join(', ')}]`);
return response.customError({
diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx
new file mode 100644
index 0000000000000..17943333d25b0
--- /dev/null
+++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.test.tsx
@@ -0,0 +1,155 @@
+/*
+ * 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 { isErrorEmbeddable, IContainer } from '../../embeddable_plugin';
+import { DashboardContainer, DashboardPanelState } from '../embeddable';
+import { getSampleDashboardInput, getSampleDashboardPanel } from '../test_helpers';
+import {
+ CONTACT_CARD_EMBEDDABLE,
+ ContactCardEmbeddableFactory,
+ ContactCardEmbeddable,
+ ContactCardEmbeddableInput,
+ ContactCardEmbeddableOutput,
+} from '../../embeddable_plugin_test_samples';
+import { coreMock } from '../../../../../core/public/mocks';
+import { CoreStart } from 'kibana/public';
+import { ClonePanelAction } from '.';
+
+// eslint-disable-next-line
+import { embeddablePluginMock } from 'src/plugins/embeddable/public/mocks';
+
+const { setup, doStart } = embeddablePluginMock.createInstance();
+setup.registerEmbeddableFactory(
+ CONTACT_CARD_EMBEDDABLE,
+ new ContactCardEmbeddableFactory((() => null) as any, {} as any)
+);
+const start = doStart();
+
+let container: DashboardContainer;
+let embeddable: ContactCardEmbeddable;
+let coreStart: CoreStart;
+beforeEach(async () => {
+ coreStart = coreMock.createStart();
+ coreStart.savedObjects.client = {
+ ...coreStart.savedObjects.client,
+ get: jest.fn().mockImplementation(() => ({ attributes: { title: 'Holy moly' } })),
+ find: jest.fn().mockImplementation(() => ({ total: 15 })),
+ create: jest.fn().mockImplementation(() => ({ id: 'brandNewSavedObject' })),
+ };
+
+ const options = {
+ ExitFullScreenButton: () => null,
+ SavedObjectFinder: () => null,
+ application: {} as any,
+ embeddable: start,
+ inspector: {} as any,
+ notifications: {} as any,
+ overlays: coreStart.overlays,
+ savedObjectMetaData: {} as any,
+ uiActions: {} as any,
+ };
+ const input = getSampleDashboardInput({
+ panels: {
+ '123': getSampleDashboardPanel({
+ explicitInput: { firstName: 'Kibanana', id: '123' },
+ type: CONTACT_CARD_EMBEDDABLE,
+ }),
+ },
+ });
+ container = new DashboardContainer(input, options);
+
+ const contactCardEmbeddable = await container.addNewEmbeddable<
+ ContactCardEmbeddableInput,
+ ContactCardEmbeddableOutput,
+ ContactCardEmbeddable
+ >(CONTACT_CARD_EMBEDDABLE, {
+ firstName: 'Kibana',
+ });
+
+ if (isErrorEmbeddable(contactCardEmbeddable)) {
+ throw new Error('Failed to create embeddable');
+ } else {
+ embeddable = contactCardEmbeddable;
+ }
+});
+
+test('Clone adds a new embeddable', async () => {
+ const dashboard = embeddable.getRoot() as IContainer;
+ const originalPanelCount = Object.keys(dashboard.getInput().panels).length;
+ const originalPanelKeySet = new Set(Object.keys(dashboard.getInput().panels));
+ const action = new ClonePanelAction(coreStart);
+ await action.execute({ embeddable });
+ expect(Object.keys(container.getInput().panels).length).toEqual(originalPanelCount + 1);
+ const newPanelId = Object.keys(container.getInput().panels).find(
+ key => !originalPanelKeySet.has(key)
+ );
+ expect(newPanelId).toBeDefined();
+ const newPanel = container.getInput().panels[newPanelId!];
+ expect(newPanel.type).toEqual(embeddable.type);
+});
+
+test('Clones an embeddable without a saved object ID', async () => {
+ const dashboard = embeddable.getRoot() as IContainer;
+ const panel = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
+ const action = new ClonePanelAction(coreStart);
+ // @ts-ignore
+ const newPanel = await action.cloneEmbeddable(panel, embeddable.type);
+ expect(newPanel.type).toEqual(embeddable.type);
+});
+
+test('Clones an embeddable with a saved object ID', async () => {
+ const dashboard = embeddable.getRoot() as IContainer;
+ const panel = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
+ panel.explicitInput.savedObjectId = 'holySavedObjectBatman';
+ const action = new ClonePanelAction(coreStart);
+ // @ts-ignore
+ const newPanel = await action.cloneEmbeddable(panel, embeddable.type);
+ expect(coreStart.savedObjects.client.get).toHaveBeenCalledTimes(1);
+ expect(coreStart.savedObjects.client.find).toHaveBeenCalledTimes(1);
+ expect(coreStart.savedObjects.client.create).toHaveBeenCalledTimes(1);
+ expect(newPanel.type).toEqual(embeddable.type);
+});
+
+test('Gets a unique title ', async () => {
+ coreStart.savedObjects.client.find = jest.fn().mockImplementation(({ search }) => {
+ if (search === '"testFirstTitle"') return { total: 1 };
+ else if (search === '"testSecondTitle"') return { total: 41 };
+ else if (search === '"testThirdTitle"') return { total: 90 };
+ });
+ const action = new ClonePanelAction(coreStart);
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testFirstTitle', embeddable.type)).toEqual(
+ 'testFirstTitle (copy)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testSecondTitle (copy 39)', embeddable.type)).toEqual(
+ 'testSecondTitle (copy 40)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testSecondTitle (copy 20)', embeddable.type)).toEqual(
+ 'testSecondTitle (copy 40)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testThirdTitle', embeddable.type)).toEqual(
+ 'testThirdTitle (copy 89)'
+ );
+ // @ts-ignore
+ expect(await action.getUniqueTitle('testThirdTitle (copy 10000)', embeddable.type)).toEqual(
+ 'testThirdTitle (copy 89)'
+ );
+});
diff --git a/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx
new file mode 100644
index 0000000000000..4d15e7e899fa8
--- /dev/null
+++ b/src/plugins/dashboard/public/application/actions/clone_panel_action.tsx
@@ -0,0 +1,158 @@
+/*
+ * 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 { CoreStart } from 'src/core/public';
+import uuid from 'uuid';
+import { ActionByType, IncompatibleActionError } from '../../ui_actions_plugin';
+import { ViewMode, PanelState, IEmbeddable } from '../../embeddable_plugin';
+import { SavedObject } from '../../../../saved_objects/public';
+import { PanelNotFoundError, EmbeddableInput } from '../../../../embeddable/public';
+import {
+ placePanelBeside,
+ IPanelPlacementBesideArgs,
+} from '../embeddable/panel/dashboard_panel_placement';
+import { DashboardPanelState, DASHBOARD_CONTAINER_TYPE, DashboardContainer } from '..';
+
+export const ACTION_CLONE_PANEL = 'clonePanel';
+
+export interface ClonePanelActionContext {
+ embeddable: IEmbeddable;
+}
+
+export class ClonePanelAction implements ActionByType {
+ public readonly type = ACTION_CLONE_PANEL;
+ public readonly id = ACTION_CLONE_PANEL;
+ public order = 11;
+
+ constructor(private core: CoreStart) {}
+
+ public getDisplayName({ embeddable }: ClonePanelActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+ return i18n.translate('dashboard.panel.clonePanel', {
+ defaultMessage: 'Clone panel',
+ });
+ }
+
+ public getIconType({ embeddable }: ClonePanelActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+ return 'copy';
+ }
+
+ public async isCompatible({ embeddable }: ClonePanelActionContext) {
+ return Boolean(
+ embeddable.getInput()?.viewMode !== ViewMode.VIEW &&
+ embeddable.getRoot() &&
+ embeddable.getRoot().isContainer &&
+ embeddable.getRoot().type === DASHBOARD_CONTAINER_TYPE
+ );
+ }
+
+ public async execute({ embeddable }: ClonePanelActionContext) {
+ if (!embeddable.getRoot() || !embeddable.getRoot().isContainer) {
+ throw new IncompatibleActionError();
+ }
+
+ const dashboard = embeddable.getRoot() as DashboardContainer;
+ const panelToClone = dashboard.getInput().panels[embeddable.id] as DashboardPanelState;
+ if (!panelToClone) {
+ throw new PanelNotFoundError();
+ }
+
+ dashboard.showPlaceholderUntil(
+ this.cloneEmbeddable(panelToClone, embeddable.type),
+ placePanelBeside,
+ {
+ width: panelToClone.gridData.w,
+ height: panelToClone.gridData.h,
+ currentPanels: dashboard.getInput().panels,
+ placeBesideId: panelToClone.explicitInput.id,
+ } as IPanelPlacementBesideArgs
+ );
+ }
+
+ private async getUniqueTitle(rawTitle: string, embeddableType: string): Promise {
+ const clonedTag = i18n.translate('dashboard.panel.title.clonedTag', {
+ defaultMessage: 'copy',
+ });
+ const cloneRegex = new RegExp(`\\(${clonedTag}\\)`, 'g');
+ const cloneNumberRegex = new RegExp(`\\(${clonedTag} [0-9]+\\)`, 'g');
+ const baseTitle = rawTitle
+ .replace(cloneNumberRegex, '')
+ .replace(cloneRegex, '')
+ .trim();
+
+ const similarSavedObjects = await this.core.savedObjects.client.find({
+ type: embeddableType,
+ perPage: 0,
+ fields: ['title'],
+ searchFields: ['title'],
+ search: `"${baseTitle}"`,
+ });
+ const similarBaseTitlesCount: number = similarSavedObjects.total - 1;
+
+ return similarBaseTitlesCount <= 0
+ ? baseTitle + ` (${clonedTag})`
+ : baseTitle + ` (${clonedTag} ${similarBaseTitlesCount})`;
+ }
+
+ private async cloneEmbeddable(
+ panelToClone: DashboardPanelState,
+ embeddableType: string
+ ): Promise> {
+ const panelState: PanelState = {
+ type: embeddableType,
+ explicitInput: {
+ ...panelToClone.explicitInput,
+ id: uuid.v4(),
+ },
+ };
+ let newTitle: string = '';
+ if (panelToClone.explicitInput.savedObjectId) {
+ // Fetch existing saved object
+ const savedObjectToClone = await this.core.savedObjects.client.get(
+ embeddableType,
+ panelToClone.explicitInput.savedObjectId
+ );
+
+ // Clone the saved object
+ newTitle = await this.getUniqueTitle(savedObjectToClone.attributes.title, embeddableType);
+ const clonedSavedObject = await this.core.savedObjects.client.create(
+ embeddableType,
+ {
+ ..._.cloneDeep(savedObjectToClone.attributes),
+ title: newTitle,
+ },
+ { references: _.cloneDeep(savedObjectToClone.references) }
+ );
+ panelState.explicitInput.savedObjectId = clonedSavedObject.id;
+ }
+ this.core.notifications.toasts.addSuccess({
+ title: i18n.translate('dashboard.panel.clonedToast', {
+ defaultMessage: 'Cloned panel',
+ }),
+ 'data-test-subj': 'addObjectToContainerSuccess',
+ });
+ return panelState;
+ }
+}
diff --git a/src/plugins/dashboard/public/application/actions/index.ts b/src/plugins/dashboard/public/application/actions/index.ts
index 23c26dbd280f8..d7a84fb79f6af 100644
--- a/src/plugins/dashboard/public/application/actions/index.ts
+++ b/src/plugins/dashboard/public/application/actions/index.ts
@@ -27,3 +27,8 @@ export {
ReplacePanelActionContext,
ACTION_REPLACE_PANEL,
} from './replace_panel_action';
+export {
+ ClonePanelAction,
+ ClonePanelActionContext,
+ ACTION_CLONE_PANEL,
+} from './clone_panel_action';
diff --git a/src/plugins/dashboard/public/application/dashboard_state_manager.ts b/src/plugins/dashboard/public/application/dashboard_state_manager.ts
index 13ba3c6d0b60d..b03ea95069a3d 100644
--- a/src/plugins/dashboard/public/application/dashboard_state_manager.ts
+++ b/src/plugins/dashboard/public/application/dashboard_state_manager.ts
@@ -34,6 +34,7 @@ import { FilterUtils } from './lib/filter_utils';
import {
DashboardAppState,
DashboardAppStateDefaults,
+ DashboardAppStateInUrl,
DashboardAppStateTransitions,
SavedDashboardPanel,
} from '../types';
@@ -165,11 +166,12 @@ export class DashboardStateManager {
});
// setup state syncing utils. state container will be synced with url into `this.STATE_STORAGE_KEY` query param
- this.stateSyncRef = syncState({
+ this.stateSyncRef = syncState({
storageKey: this.STATE_STORAGE_KEY,
stateContainer: {
...this.stateContainer,
- set: (state: DashboardAppState | null) => {
+ get: () => this.toUrlState(this.stateContainer.get()),
+ set: (state: DashboardAppStateInUrl | null) => {
// sync state required state container to be able to handle null
// overriding set() so it could handle null coming from url
if (state) {
@@ -558,9 +560,9 @@ export class DashboardStateManager {
*/
private saveState({ replace }: { replace: boolean }): boolean {
// schedules setting current state to url
- this.kbnUrlStateStorage.set(
+ this.kbnUrlStateStorage.set(
this.STATE_STORAGE_KEY,
- this.stateContainer.get()
+ this.toUrlState(this.stateContainer.get())
);
// immediately forces scheduled updates and changes location
return this.kbnUrlStateStorage.flush({ replace });
@@ -620,4 +622,13 @@ export class DashboardStateManager {
const current = _.omit(this.stateContainer.get(), propsToIgnore);
return !_.isEqual(initial, current);
}
+
+ private toUrlState(state: DashboardAppState): DashboardAppStateInUrl {
+ if (state.viewMode === ViewMode.VIEW) {
+ const { panels, ...stateWithoutPanels } = state;
+ return stateWithoutPanels;
+ }
+
+ return state;
+ }
}
diff --git a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx
index 50089f1f061f4..8346fd900caef 100644
--- a/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx
+++ b/src/plugins/dashboard/public/application/embeddable/dashboard_container.tsx
@@ -23,6 +23,7 @@ import { I18nProvider } from '@kbn/i18n/react';
import { RefreshInterval, TimeRange, Query, Filter } from 'src/plugins/data/public';
import { CoreStart } from 'src/core/public';
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
+import uuid from 'uuid';
import { UiActionsStart } from '../../ui_actions_plugin';
import {
Container,
@@ -32,6 +33,7 @@ import {
EmbeddableFactory,
IEmbeddable,
EmbeddableStart,
+ PanelState,
} from '../../embeddable_plugin';
import { DASHBOARD_CONTAINER_TYPE } from './dashboard_constants';
import { createPanelState } from './panel';
@@ -42,6 +44,8 @@ import {
KibanaReactContext,
KibanaReactContextValue,
} from '../../../../kibana_react/public';
+import { PLACEHOLDER_EMBEDDABLE } from './placeholder';
+import { PanelPlacementMethod, IPanelPlacementArgs } from './panel/dashboard_panel_placement';
export interface DashboardContainerInput extends ContainerInput {
viewMode: ViewMode;
@@ -120,7 +124,61 @@ export class DashboardContainer extends Container = {}
): DashboardPanelState {
const panelState = super.createNewPanelState(factory, partial);
- return createPanelState(panelState, Object.values(this.input.panels));
+ return createPanelState(panelState, this.input.panels);
+ }
+
+ public showPlaceholderUntil(
+ newStateComplete: Promise>,
+ placementMethod?: PanelPlacementMethod,
+ placementArgs?: TPlacementMethodArgs
+ ): void {
+ const originalPanelState = {
+ type: PLACEHOLDER_EMBEDDABLE,
+ explicitInput: {
+ id: uuid.v4(),
+ disabledActions: [
+ 'ACTION_CUSTOMIZE_PANEL',
+ 'CUSTOM_TIME_RANGE',
+ 'clonePanel',
+ 'replacePanel',
+ 'togglePanel',
+ ],
+ },
+ } as PanelState;
+ const placeholderPanelState = createPanelState(
+ originalPanelState,
+ this.input.panels,
+ placementMethod,
+ placementArgs
+ );
+ this.updateInput({
+ panels: {
+ ...this.input.panels,
+ [placeholderPanelState.explicitInput.id]: placeholderPanelState,
+ },
+ });
+ newStateComplete.then((newPanelState: Partial) => {
+ const finalPanels = { ...this.input.panels };
+ delete finalPanels[placeholderPanelState.explicitInput.id];
+ const newPanelId = newPanelState.explicitInput?.id
+ ? newPanelState.explicitInput.id
+ : uuid.v4();
+ finalPanels[newPanelId] = {
+ ...placeholderPanelState,
+ ...newPanelState,
+ gridData: {
+ ...placeholderPanelState.gridData,
+ i: newPanelId,
+ },
+ explicitInput: {
+ ...newPanelState.explicitInput,
+ id: newPanelId,
+ },
+ };
+ this.updateInput({
+ panels: finalPanels,
+ });
+ });
}
public render(dom: HTMLElement) {
diff --git a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts
index 409cae8b49a53..7c11ac8a5031b 100644
--- a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts
+++ b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.test.ts
@@ -26,7 +26,7 @@ import { CONTACT_CARD_EMBEDDABLE } from '../../../embeddable_plugin_test_samples
interface TestInput extends EmbeddableInput {
test: string;
}
-const panels: DashboardPanelState[] = [];
+const panels: { [key: string]: DashboardPanelState } = {};
test('createPanelState adds a new panel state in 0,0 position', () => {
const panelState = createPanelState(
@@ -34,7 +34,7 @@ test('createPanelState adds a new panel state in 0,0 position', () => {
type: CONTACT_CARD_EMBEDDABLE,
explicitInput: { test: 'hi', id: '123' },
},
- []
+ panels
);
expect(panelState.explicitInput.test).toBe('hi');
expect(panelState.type).toBe(CONTACT_CARD_EMBEDDABLE);
@@ -44,7 +44,7 @@ test('createPanelState adds a new panel state in 0,0 position', () => {
expect(panelState.gridData.h).toBe(DEFAULT_PANEL_HEIGHT);
expect(panelState.gridData.w).toBe(DEFAULT_PANEL_WIDTH);
- panels.push(panelState);
+ panels[panelState.explicitInput.id] = panelState;
});
test('createPanelState adds a second new panel state', () => {
@@ -58,7 +58,7 @@ test('createPanelState adds a second new panel state', () => {
expect(panelState.gridData.h).toBe(DEFAULT_PANEL_HEIGHT);
expect(panelState.gridData.w).toBe(DEFAULT_PANEL_WIDTH);
- panels.push(panelState);
+ panels[panelState.explicitInput.id] = panelState;
});
test('createPanelState adds a third new panel state', () => {
@@ -74,17 +74,17 @@ test('createPanelState adds a third new panel state', () => {
expect(panelState.gridData.h).toBe(DEFAULT_PANEL_HEIGHT);
expect(panelState.gridData.w).toBe(DEFAULT_PANEL_WIDTH);
- panels.push(panelState);
+ panels[panelState.explicitInput.id] = panelState;
});
test('createPanelState adds a new panel state in the top most position', () => {
- const panelsWithEmptySpace = panels.filter(panel => panel.gridData.x === 0);
+ delete panels['456'];
const panelState = createPanelState(
{
type: CONTACT_CARD_EMBEDDABLE,
explicitInput: { test: 'bye', id: '987' },
},
- panelsWithEmptySpace
+ panels
);
expect(panelState.gridData.x).toBe(DEFAULT_PANEL_WIDTH);
expect(panelState.gridData.y).toBe(0);
diff --git a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts
index f3a48368fe1b3..79116a57869d3 100644
--- a/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts
+++ b/src/plugins/dashboard/public/application/embeddable/panel/create_panel_state.ts
@@ -19,98 +19,45 @@
import _ from 'lodash';
import { PanelState, EmbeddableInput } from '../../../embeddable_plugin';
-import {
- DASHBOARD_GRID_COLUMN_COUNT,
- DEFAULT_PANEL_HEIGHT,
- DEFAULT_PANEL_WIDTH,
-} from '../dashboard_constants';
+import { DEFAULT_PANEL_HEIGHT, DEFAULT_PANEL_WIDTH } from '../dashboard_constants';
import { DashboardPanelState } from '../types';
-
-// Look for the smallest y and x value where the default panel will fit.
-function findTopLeftMostOpenSpace(
- width: number,
- height: number,
- currentPanels: DashboardPanelState[]
-) {
- let maxY = -1;
-
- currentPanels.forEach(panel => {
- maxY = Math.max(panel.gridData.y + panel.gridData.h, maxY);
- });
-
- // Handle case of empty grid.
- if (maxY < 0) {
- return { x: 0, y: 0 };
- }
-
- const grid = new Array(maxY);
- for (let y = 0; y < maxY; y++) {
- grid[y] = new Array(DASHBOARD_GRID_COLUMN_COUNT).fill(0);
- }
-
- currentPanels.forEach(panel => {
- for (let x = panel.gridData.x; x < panel.gridData.x + panel.gridData.w; x++) {
- for (let y = panel.gridData.y; y < panel.gridData.y + panel.gridData.h; y++) {
- const row = grid[y];
- if (row === undefined) {
- throw new Error(
- `Attempted to access a row that doesn't exist at ${y} for panel ${JSON.stringify(
- panel
- )}`
- );
- }
- grid[y][x] = 1;
- }
- }
- });
-
- for (let y = 0; y < maxY; y++) {
- for (let x = 0; x < DASHBOARD_GRID_COLUMN_COUNT; x++) {
- if (grid[y][x] === 1) {
- // Space is filled
- continue;
- } else {
- for (let h = y; h < Math.min(y + height, maxY); h++) {
- for (let w = x; w < Math.min(x + width, DASHBOARD_GRID_COLUMN_COUNT); w++) {
- const spaceIsEmpty = grid[h][w] === 0;
- const fitsPanelWidth = w === x + width - 1;
- // If the panel is taller than any other panel in the current grid, it can still fit in the space, hence
- // we check the minimum of maxY and the panel height.
- const fitsPanelHeight = h === Math.min(y + height - 1, maxY - 1);
-
- if (spaceIsEmpty && fitsPanelWidth && fitsPanelHeight) {
- // Found space
- return { x, y };
- } else if (grid[h][w] === 1) {
- // x, y spot doesn't work, break.
- break;
- }
- }
- }
- }
- }
- }
- return { x: 0, y: maxY };
-}
+import {
+ IPanelPlacementArgs,
+ findTopLeftMostOpenSpace,
+ PanelPlacementMethod,
+} from './dashboard_panel_placement';
/**
* Creates and initializes a basic panel state.
*/
-export function createPanelState(
+export function createPanelState<
+ TEmbeddableInput extends EmbeddableInput,
+ TPlacementMethodArgs extends IPanelPlacementArgs = IPanelPlacementArgs
+>(
panelState: PanelState,
- currentPanels: DashboardPanelState[]
+ currentPanels: { [key: string]: DashboardPanelState },
+ placementMethod?: PanelPlacementMethod,
+ placementArgs?: TPlacementMethodArgs
): DashboardPanelState {
- const { x, y } = findTopLeftMostOpenSpace(
- DEFAULT_PANEL_WIDTH,
- DEFAULT_PANEL_HEIGHT,
- currentPanels
- );
+ const defaultPlacementArgs = {
+ width: DEFAULT_PANEL_WIDTH,
+ height: DEFAULT_PANEL_HEIGHT,
+ currentPanels,
+ };
+ const finalPlacementArgs = placementArgs
+ ? {
+ ...defaultPlacementArgs,
+ ...placementArgs,
+ }
+ : defaultPlacementArgs;
+
+ const gridDataLocation = placementMethod
+ ? placementMethod(finalPlacementArgs as TPlacementMethodArgs)
+ : findTopLeftMostOpenSpace(defaultPlacementArgs);
+
return {
gridData: {
- w: DEFAULT_PANEL_WIDTH,
- h: DEFAULT_PANEL_HEIGHT,
- x,
- y,
+ ...gridDataLocation,
i: panelState.explicitInput.id,
},
...panelState,
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
new file mode 100644
index 0000000000000..70a6c83418587
--- /dev/null
+++ b/src/plugins/dashboard/public/application/embeddable/panel/dashboard_panel_placement.ts
@@ -0,0 +1,167 @@
+/*
+ * 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 { PanelNotFoundError } from '../../../embeddable_plugin';
+import { DashboardPanelState, GridData, DASHBOARD_GRID_COLUMN_COUNT } from '..';
+
+export type PanelPlacementMethod = (
+ args: PlacementArgs
+) => Omit;
+
+export interface IPanelPlacementArgs {
+ width: number;
+ height: number;
+ currentPanels: { [key: string]: DashboardPanelState };
+}
+
+export interface IPanelPlacementBesideArgs extends IPanelPlacementArgs {
+ placeBesideId: string;
+}
+
+// Look for the smallest y and x value where the default panel will fit.
+export function findTopLeftMostOpenSpace({
+ width,
+ height,
+ currentPanels,
+}: IPanelPlacementArgs): Omit {
+ let maxY = -1;
+
+ const currentPanelsArray = Object.values(currentPanels);
+ currentPanelsArray.forEach(panel => {
+ maxY = Math.max(panel.gridData.y + panel.gridData.h, maxY);
+ });
+
+ // Handle case of empty grid.
+ if (maxY < 0) {
+ return { x: 0, y: 0, w: width, h: height };
+ }
+
+ const grid = new Array(maxY);
+ for (let y = 0; y < maxY; y++) {
+ grid[y] = new Array(DASHBOARD_GRID_COLUMN_COUNT).fill(0);
+ }
+
+ currentPanelsArray.forEach(panel => {
+ for (let x = panel.gridData.x; x < panel.gridData.x + panel.gridData.w; x++) {
+ for (let y = panel.gridData.y; y < panel.gridData.y + panel.gridData.h; y++) {
+ const row = grid[y];
+ if (row === undefined) {
+ throw new Error(
+ `Attempted to access a row that doesn't exist at ${y} for panel ${JSON.stringify(
+ panel
+ )}`
+ );
+ }
+ grid[y][x] = 1;
+ }
+ }
+ });
+
+ for (let y = 0; y < maxY; y++) {
+ for (let x = 0; x < DASHBOARD_GRID_COLUMN_COUNT; x++) {
+ if (grid[y][x] === 1) {
+ // Space is filled
+ continue;
+ } else {
+ for (let h = y; h < Math.min(y + height, maxY); h++) {
+ for (let w = x; w < Math.min(x + width, DASHBOARD_GRID_COLUMN_COUNT); w++) {
+ const spaceIsEmpty = grid[h][w] === 0;
+ const fitsPanelWidth = w === x + width - 1;
+ // If the panel is taller than any other panel in the current grid, it can still fit in the space, hence
+ // we check the minimum of maxY and the panel height.
+ const fitsPanelHeight = h === Math.min(y + height - 1, maxY - 1);
+
+ if (spaceIsEmpty && fitsPanelWidth && fitsPanelHeight) {
+ // Found space
+ return { x, y, w: width, h: height };
+ } else if (grid[h][w] === 1) {
+ // x, y spot doesn't work, break.
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return { x: 0, y: maxY, w: width, h: height };
+}
+
+interface IplacementDirection {
+ grid: Omit;
+ fits: boolean;
+}
+
+export function placePanelBeside({
+ width,
+ height,
+ currentPanels,
+ placeBesideId,
+}: IPanelPlacementBesideArgs): Omit {
+ // const clonedPanels = _.cloneDeep(currentPanels);
+ if (!placeBesideId) {
+ throw new Error('Place beside method called without placeBesideId');
+ }
+ const panelToPlaceBeside = currentPanels[placeBesideId];
+ if (!panelToPlaceBeside) {
+ throw new PanelNotFoundError();
+ }
+ const beside = panelToPlaceBeside.gridData;
+ const otherPanels: GridData[] = [];
+ _.forOwn(currentPanels, (panel: DashboardPanelState, key: string | undefined) => {
+ otherPanels.push(panel.gridData);
+ });
+
+ const possiblePlacementDirections: IplacementDirection[] = [
+ { grid: { x: beside.x + beside.w, y: beside.y, w: width, h: height }, fits: true }, // right
+ { grid: { x: beside.x - width, y: beside.y, w: width, h: height }, fits: true }, // left
+ { grid: { x: beside.x, y: beside.y + beside.h, w: width, h: height }, fits: true }, // bottom
+ ];
+
+ for (const direction of possiblePlacementDirections) {
+ if (
+ direction.grid.x >= 0 &&
+ direction.grid.x + direction.grid.w <= DASHBOARD_GRID_COLUMN_COUNT &&
+ direction.grid.y >= 0
+ ) {
+ const intersection = otherPanels.some((currentPanelGrid: GridData) => {
+ return (
+ direction.grid.x + direction.grid.w > currentPanelGrid.x &&
+ direction.grid.x < currentPanelGrid.x + currentPanelGrid.w &&
+ direction.grid.y < currentPanelGrid.y + currentPanelGrid.h &&
+ direction.grid.y + direction.grid.h > currentPanelGrid.y
+ );
+ });
+ if (!intersection) {
+ return direction.grid;
+ }
+ } else {
+ direction.fits = false;
+ }
+ }
+ // if we get here that means there is no blank space around the panel we are placing beside. This means it's time to mess up the dashboard's groove. Fun!
+ const [, , bottomPlacement] = possiblePlacementDirections;
+ for (const currentPanelGrid of otherPanels) {
+ if (bottomPlacement.grid.y <= currentPanelGrid.y) {
+ const movedPanel = _.cloneDeep(currentPanels[currentPanelGrid.i]);
+ movedPanel.gridData.y = movedPanel.gridData.y + bottomPlacement.grid.h;
+ currentPanels[currentPanelGrid.i] = movedPanel;
+ }
+ }
+ return bottomPlacement.grid;
+}
diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts b/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts
new file mode 100644
index 0000000000000..7c65d50ce3fea
--- /dev/null
+++ b/src/plugins/dashboard/public/application/embeddable/placeholder/index.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 * from './placeholder_embeddable';
+export * from './placeholder_embeddable_factory';
diff --git a/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx
new file mode 100644
index 0000000000000..1a5c3386bdeda
--- /dev/null
+++ b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable.tsx
@@ -0,0 +1,52 @@
+/*
+ * 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 React from 'react';
+import ReactDOM from 'react-dom';
+import { EuiLoadingChart } from '@elastic/eui';
+import classNames from 'classnames';
+import { Embeddable, EmbeddableInput, IContainer } from '../../../embeddable_plugin';
+
+export const PLACEHOLDER_EMBEDDABLE = 'placeholder';
+
+export class PlaceholderEmbeddable extends Embeddable {
+ public readonly type = PLACEHOLDER_EMBEDDABLE;
+ private node?: HTMLElement;
+
+ constructor(initialInput: EmbeddableInput, parent?: IContainer) {
+ super(initialInput, {}, parent);
+ this.input = initialInput;
+ }
+ public render(node: HTMLElement) {
+ if (this.node) {
+ ReactDOM.unmountComponentAtNode(this.node);
+ }
+ this.node = node;
+
+ const classes = classNames('embPanel', 'embPanel-isLoading');
+ ReactDOM.render(
+
+
+
,
+ node
+ );
+ }
+
+ public reload() {}
+}
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
new file mode 100644
index 0000000000000..30a93989649a7
--- /dev/null
+++ b/src/plugins/dashboard/public/application/embeddable/placeholder/placeholder_embeddable_factory.ts
@@ -0,0 +1,45 @@
+/*
+ * 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 {
+ EmbeddableFactoryDefinition,
+ EmbeddableInput,
+ IContainer,
+} from '../../../embeddable_plugin';
+import { PlaceholderEmbeddable, PLACEHOLDER_EMBEDDABLE } from './placeholder_embeddable';
+
+export class PlaceholderEmbeddableFactory implements EmbeddableFactoryDefinition {
+ public readonly type = PLACEHOLDER_EMBEDDABLE;
+
+ public async isEditable() {
+ return false;
+ }
+
+ public async create(initialInput: EmbeddableInput, parent?: IContainer) {
+ return new PlaceholderEmbeddable(initialInput, parent);
+ }
+
+ public getDisplayName() {
+ return i18n.translate('dashboard.placeholder.factory.displayName', {
+ defaultMessage: 'placeholder',
+ });
+ }
+}
diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx
index 322d734d9f39f..203c784d9df4e 100644
--- a/src/plugins/dashboard/public/plugin.tsx
+++ b/src/plugins/dashboard/public/plugin.tsx
@@ -67,9 +67,12 @@ import {
ExpandPanelActionContext,
ReplacePanelAction,
ReplacePanelActionContext,
+ ClonePanelAction,
+ ClonePanelActionContext,
ACTION_EXPAND_PANEL,
ACTION_REPLACE_PANEL,
RenderDeps,
+ ACTION_CLONE_PANEL,
} from './application';
import {
DashboardAppLinkGeneratorState,
@@ -78,6 +81,7 @@ import {
} from './url_generator';
import { createSavedDashboardLoader } from './saved_dashboards';
import { DashboardConstants } from './dashboard_constants';
+import { PlaceholderEmbeddableFactory } from './application/embeddable/placeholder';
declare module '../../share/public' {
export interface UrlGeneratorStateMapping {
@@ -115,6 +119,7 @@ declare module '../../../plugins/ui_actions/public' {
export interface ActionContextMapping {
[ACTION_EXPAND_PANEL]: ExpandPanelActionContext;
[ACTION_REPLACE_PANEL]: ReplacePanelActionContext;
+ [ACTION_CLONE_PANEL]: ClonePanelActionContext;
}
}
@@ -173,6 +178,9 @@ export class DashboardPlugin
const factory = new DashboardContainerFactory(getStartServices);
embeddable.registerEmbeddableFactory(factory.type, factory);
+ const placeholderFactory = new PlaceholderEmbeddableFactory();
+ embeddable.registerEmbeddableFactory(placeholderFactory.type, placeholderFactory);
+
const { appMounted, appUnMounted, stop: stopUrlTracker } = createKbnUrlTracker({
baseUrl: core.http.basePath.prepend('/app/kibana'),
defaultSubUrl: `#${DashboardConstants.LANDING_PAGE_PATH}`,
@@ -297,6 +305,11 @@ export class DashboardPlugin
);
uiActions.registerAction(changeViewAction);
uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction);
+
+ const clonePanelAction = new ClonePanelAction(core);
+ uiActions.registerAction(clonePanelAction);
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, clonePanelAction);
+
const savedDashboardLoader = createSavedDashboardLoader({
savedObjectsClient: core.savedObjects.client,
indexPatterns,
diff --git a/src/plugins/dashboard/public/types.ts b/src/plugins/dashboard/public/types.ts
index 7bccd3de6eca8..d96d2cdf75626 100644
--- a/src/plugins/dashboard/public/types.ts
+++ b/src/plugins/dashboard/public/types.ts
@@ -152,6 +152,14 @@ export type DashboardAppStateDefaults = DashboardAppState & {
description?: string;
};
+/**
+ * In URL panels are optional,
+ * Panels are not added to the URL when in "view" mode
+ */
+export type DashboardAppStateInUrl = Omit & {
+ panels?: SavedDashboardPanel[];
+};
+
export interface DashboardAppStateTransitions {
set: (
state: DashboardAppState
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index 06a46065baa84..05a4141483587 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -293,6 +293,7 @@ import {
convertIPRangeToString,
intervalOptions, // only used in Discover
isDateHistogramBucketAggConfig,
+ isNumberType,
isStringType,
isType,
parentPipelineType,
@@ -363,7 +364,6 @@ export {
SearchRequest,
SearchResponse,
SearchError,
- SearchStrategyProvider,
ISearchSource,
SearchSource,
createSearchSource,
@@ -393,6 +393,7 @@ export const search = {
InvalidEsCalendarIntervalError,
InvalidEsIntervalFormatError,
isDateHistogramBucketAggConfig,
+ isNumberType,
isStringType,
isType,
isValidEsInterval,
diff --git a/src/plugins/data/public/public.api.md b/src/plugins/data/public/public.api.md
index 6e06d063e7ebe..6383f61864146 100644
--- a/src/plugins/data/public/public.api.md
+++ b/src/plugins/data/public/public.api.md
@@ -1545,8 +1545,9 @@ export const search: {
InvalidEsCalendarIntervalError: typeof InvalidEsCalendarIntervalError;
InvalidEsIntervalFormatError: typeof InvalidEsIntervalFormatError;
isDateHistogramBucketAggConfig: typeof isDateHistogramBucketAggConfig;
+ isNumberType: (agg: import("./search").AggConfig) => boolean;
isStringType: (agg: import("./search").AggConfig) => boolean;
- isType: (type: string) => (agg: import("./search").AggConfig) => boolean;
+ isType: (...types: string[]) => (agg: import("./search").AggConfig) => boolean;
isValidEsInterval: typeof isValidEsInterval;
isValidInterval: typeof isValidInterval;
parentPipelineType: string;
@@ -1724,21 +1725,6 @@ export interface SearchSourceFields {
version?: boolean;
}
-// Warning: (ae-missing-release-tag) "SearchStrategyProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export interface SearchStrategyProvider {
- // (undocumented)
- id: string;
- // (undocumented)
- isViable: (indexPattern: IndexPattern) => boolean;
- // Warning: (ae-forgotten-export) The symbol "SearchStrategySearchParams" needs to be exported by the entry point index.d.ts
- // Warning: (ae-forgotten-export) The symbol "SearchStrategyResponse" needs to be exported by the entry point index.d.ts
- //
- // (undocumented)
- search: (params: SearchStrategySearchParams) => SearchStrategyResponse;
-}
-
// Warning: (ae-missing-release-tag) "SortDirection" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -1899,11 +1885,11 @@ export type TSearchStrategyProvider = (context: ISearc
// 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:405:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:410:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:413:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" 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/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
diff --git a/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts b/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts
index 0beeb1c372275..116f8cfad60f6 100644
--- a/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts
+++ b/src/plugins/data/public/search/aggs/buckets/migrate_include_exclude_format.ts
@@ -21,20 +21,22 @@ import { isString, isObject } from 'lodash';
import { IBucketAggConfig, BucketAggType, BucketAggParam } from './bucket_agg_type';
import { IAggConfig } from '../agg_config';
-export const isType = (type: string) => {
+export const isType = (...types: string[]) => {
return (agg: IAggConfig): boolean => {
const field = agg.params.field;
- return field && field.type === type;
+ return types.some(type => field && field.type === type);
};
};
+export const isNumberType = isType('number');
export const isStringType = isType('string');
+export const isStringOrNumberType = isType('string', 'number');
export const migrateIncludeExcludeFormat = {
serialize(this: BucketAggParam, value: any, agg: IBucketAggConfig) {
if (this.shouldShow && !this.shouldShow(agg)) return;
- if (!value || isString(value)) return value;
+ if (!value || isString(value) || Array.isArray(value)) return value;
else return value.pattern;
},
write(
@@ -44,7 +46,12 @@ export const migrateIncludeExcludeFormat = {
) {
const value = aggConfig.getParam(this.name);
- if (isObject(value)) {
+ if (Array.isArray(value) && value.length > 0 && isNumberType(aggConfig)) {
+ const parsedValue = value.filter((val): val is number => Number.isFinite(val));
+ if (parsedValue.length) {
+ output.params[this.name] = parsedValue;
+ }
+ } else if (isObject(value)) {
output.params[this.name] = value.pattern;
} else if (value && isStringType(aggConfig)) {
output.params[this.name] = value;
diff --git a/src/plugins/data/public/search/aggs/buckets/terms.test.ts b/src/plugins/data/public/search/aggs/buckets/terms.test.ts
index 0dc052bd1fdf6..9769efb6da749 100644
--- a/src/plugins/data/public/search/aggs/buckets/terms.test.ts
+++ b/src/plugins/data/public/search/aggs/buckets/terms.test.ts
@@ -75,5 +75,65 @@ describe('Terms Agg', () => {
expect(params.include).toBe('404');
expect(params.exclude).toBe('400');
});
+
+ test('accepts string from string field type and writes this value', () => {
+ const aggConfigs = getAggConfigs({
+ include: 'include value',
+ exclude: 'exclude value',
+ field: {
+ name: 'string_field',
+ type: 'string',
+ },
+ orderAgg: {
+ type: 'count',
+ },
+ });
+
+ const { [BUCKET_TYPES.TERMS]: params } = aggConfigs.aggs[0].toDsl();
+
+ expect(params.field).toBe('string_field');
+ expect(params.include).toBe('include value');
+ expect(params.exclude).toBe('exclude value');
+ });
+
+ test('accepts empty array from number field type and does not write a value', () => {
+ const aggConfigs = getAggConfigs({
+ include: [],
+ exclude: [],
+ field: {
+ name: 'empty_number_field',
+ type: 'number',
+ },
+ orderAgg: {
+ type: 'count',
+ },
+ });
+
+ const { [BUCKET_TYPES.TERMS]: params } = aggConfigs.aggs[0].toDsl();
+
+ expect(params.field).toBe('empty_number_field');
+ expect(params.include).toBe(undefined);
+ expect(params.exclude).toBe(undefined);
+ });
+
+ test('filters array with empty strings from number field type and writes only numbers', () => {
+ const aggConfigs = getAggConfigs({
+ include: [1.1, 2, '', 3.33, ''],
+ exclude: ['', 4, 5.555, '', 6],
+ field: {
+ name: 'number_field',
+ type: 'number',
+ },
+ orderAgg: {
+ type: 'count',
+ },
+ });
+
+ const { [BUCKET_TYPES.TERMS]: params } = aggConfigs.aggs[0].toDsl();
+
+ expect(params.field).toBe('number_field');
+ expect(params.include).toStrictEqual([1.1, 2, 3.33]);
+ expect(params.exclude).toStrictEqual([4, 5.555, 6]);
+ });
});
});
diff --git a/src/plugins/data/public/search/aggs/buckets/terms.ts b/src/plugins/data/public/search/aggs/buckets/terms.ts
index 5baa38af0e8d6..698e0dfb1d340 100644
--- a/src/plugins/data/public/search/aggs/buckets/terms.ts
+++ b/src/plugins/data/public/search/aggs/buckets/terms.ts
@@ -22,7 +22,10 @@ import { i18n } from '@kbn/i18n';
import { BucketAggType, IBucketAggConfig } from './bucket_agg_type';
import { BUCKET_TYPES } from './bucket_agg_types';
import { createFilterTerms } from './create_filter/terms';
-import { isStringType, migrateIncludeExcludeFormat } from './migrate_include_exclude_format';
+import {
+ isStringOrNumberType,
+ migrateIncludeExcludeFormat,
+} from './migrate_include_exclude_format';
import { IAggConfigs } from '../agg_configs';
import { Adapters } from '../../../../../inspector/public';
@@ -266,7 +269,7 @@ export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDe
}),
type: 'string',
advanced: true,
- shouldShow: isStringType,
+ shouldShow: isStringOrNumberType,
...migrateIncludeExcludeFormat,
},
{
@@ -276,7 +279,7 @@ export const getTermsBucketAgg = ({ getInternalStartServices }: TermsBucketAggDe
}),
type: 'string',
advanced: true,
- shouldShow: isStringType,
+ shouldShow: isStringOrNumberType,
...migrateIncludeExcludeFormat,
},
],
diff --git a/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts b/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts
index d1d591771743c..e807e084c683a 100644
--- a/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts
+++ b/src/plugins/data/public/search/aggs/test_helpers/mock_data_services.ts
@@ -18,8 +18,8 @@
*/
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { coreMock } from '../../../../../../../src/core/public/mocks';
-import { dataPluginMock } from '../../../../public/mocks';
+import { coreMock } from '../../../../../../core/public/mocks';
+import { dataPluginMock } from '../../../mocks';
import {
setFieldFormats,
setIndexPatterns,
@@ -29,7 +29,7 @@ import {
setQueryService,
setSearchService,
setUiSettings,
-} from '../../../../public/services';
+} from '../../../services';
/**
* Testing helper which calls all of the service setters used in the
@@ -49,4 +49,9 @@ export function mockDataServices() {
setQueryService(data.query);
setSearchService(data.search);
setUiSettings(core.uiSettings);
+
+ return {
+ core,
+ data,
+ };
}
diff --git a/src/plugins/data/public/search/fetch/get_search_params.test.ts b/src/plugins/data/public/search/fetch/get_search_params.test.ts
new file mode 100644
index 0000000000000..edf18405e8ff7
--- /dev/null
+++ b/src/plugins/data/public/search/fetch/get_search_params.test.ts
@@ -0,0 +1,70 @@
+/*
+ * 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 { getSearchParams } from './get_search_params';
+import { IUiSettingsClient } from 'kibana/public';
+
+function getConfigStub(config: any = {}) {
+ return {
+ get: key => config[key],
+ } as IUiSettingsClient;
+}
+
+describe('getSearchParams', () => {
+ test('includes rest_total_hits_as_int', () => {
+ const config = getConfigStub();
+ const searchParams = getSearchParams(config);
+ expect(searchParams.rest_total_hits_as_int).toBe(true);
+ });
+
+ test('includes ignore_unavailable', () => {
+ const config = getConfigStub();
+ const searchParams = getSearchParams(config);
+ expect(searchParams.ignore_unavailable).toBe(true);
+ });
+
+ test('includes ignore_throttled according to search:includeFrozen', () => {
+ let config = getConfigStub({ 'search:includeFrozen': true });
+ let searchParams = getSearchParams(config);
+ expect(searchParams.ignore_throttled).toBe(false);
+
+ config = getConfigStub({ 'search:includeFrozen': false });
+ searchParams = getSearchParams(config);
+ expect(searchParams.ignore_throttled).toBe(true);
+ });
+
+ test('includes max_concurrent_shard_requests according to courier:maxConcurrentShardRequests', () => {
+ let config = getConfigStub({ 'courier:maxConcurrentShardRequests': 0 });
+ let searchParams = getSearchParams(config);
+ expect(searchParams.max_concurrent_shard_requests).toBe(undefined);
+
+ config = getConfigStub({ 'courier:maxConcurrentShardRequests': 5 });
+ searchParams = getSearchParams(config);
+ expect(searchParams.max_concurrent_shard_requests).toBe(5);
+ });
+
+ test('includes timeout according to esShardTimeout if greater than 0', () => {
+ const config = getConfigStub();
+ let searchParams = getSearchParams(config, 0);
+ expect(searchParams.timeout).toBe(undefined);
+
+ searchParams = getSearchParams(config, 100);
+ expect(searchParams.timeout).toBe('100ms');
+ });
+});
diff --git a/src/plugins/data/public/search/search_strategy/get_search_params.ts b/src/plugins/data/public/search/fetch/get_search_params.ts
similarity index 81%
rename from src/plugins/data/public/search/search_strategy/get_search_params.ts
rename to src/plugins/data/public/search/fetch/get_search_params.ts
index 9fb8f2c728c6f..f0c43bd2e74cd 100644
--- a/src/plugins/data/public/search/search_strategy/get_search_params.ts
+++ b/src/plugins/data/public/search/fetch/get_search_params.ts
@@ -17,18 +17,10 @@
* under the License.
*/
-import { IUiSettingsClient } from '../../../../../core/public';
+import { IUiSettingsClient } from 'kibana/public';
const sessionId = Date.now();
-export function getMSearchParams(config: IUiSettingsClient) {
- return {
- rest_total_hits_as_int: true,
- ignore_throttled: getIgnoreThrottled(config),
- max_concurrent_shard_requests: getMaxConcurrentShardRequests(config),
- };
-}
-
export function getSearchParams(config: IUiSettingsClient, esShardTimeout: number = 0) {
return {
rest_total_hits_as_int: true,
@@ -40,11 +32,11 @@ export function getSearchParams(config: IUiSettingsClient, esShardTimeout: numbe
};
}
-function getIgnoreThrottled(config: IUiSettingsClient) {
+export function getIgnoreThrottled(config: IUiSettingsClient) {
return !config.get('search:includeFrozen');
}
-function getMaxConcurrentShardRequests(config: IUiSettingsClient) {
+export function getMaxConcurrentShardRequests(config: IUiSettingsClient) {
const maxConcurrentShardRequests = config.get('courier:maxConcurrentShardRequests');
return maxConcurrentShardRequests > 0 ? maxConcurrentShardRequests : undefined;
}
diff --git a/src/plugins/data/public/search/fetch/index.ts b/src/plugins/data/public/search/fetch/index.ts
index 8a80b716add32..39845ec31bfaa 100644
--- a/src/plugins/data/public/search/fetch/index.ts
+++ b/src/plugins/data/public/search/fetch/index.ts
@@ -18,5 +18,14 @@
*/
export * from './types';
-export { fetchSoon } from './fetch_soon';
-export { RequestFailure } from './errors';
+export {
+ getSearchParams,
+ getPreference,
+ getTimeout,
+ getIgnoreThrottled,
+ getMaxConcurrentShardRequests,
+} from './get_search_params';
+
+export { SearchError, getSearchErrorType } from './search_error';
+export { RequestFailure } from './request_error';
+export { handleResponse } from './handle_response';
diff --git a/src/plugins/data/public/search/fetch/errors.ts b/src/plugins/data/public/search/fetch/request_error.ts
similarity index 100%
rename from src/plugins/data/public/search/fetch/errors.ts
rename to src/plugins/data/public/search/fetch/request_error.ts
diff --git a/src/plugins/data/public/search/search_strategy/search_error.ts b/src/plugins/data/public/search/fetch/search_error.ts
similarity index 100%
rename from src/plugins/data/public/search/search_strategy/search_error.ts
rename to src/plugins/data/public/search/fetch/search_error.ts
diff --git a/src/plugins/data/public/search/fetch/types.ts b/src/plugins/data/public/search/fetch/types.ts
index e8de0576b8a72..475b2abbc019f 100644
--- a/src/plugins/data/public/search/fetch/types.ts
+++ b/src/plugins/data/public/search/fetch/types.ts
@@ -20,6 +20,9 @@
import { IUiSettingsClient } from '../../../../../core/public';
import { ISearchStart } from '../types';
+export type SearchRequest = any;
+export type SearchResponse = any;
+
export interface FetchOptions {
abortSignal?: AbortSignal;
searchStrategyId?: string;
diff --git a/src/plugins/data/public/search/index.ts b/src/plugins/data/public/search/index.ts
index cce973d632f41..75c0eb8b6f022 100644
--- a/src/plugins/data/public/search/index.ts
+++ b/src/plugins/data/public/search/index.ts
@@ -44,9 +44,13 @@ export { esSearchStrategyProvider, getEsPreference } from './es_search';
export { IKibanaSearchResponse, IKibanaSearchRequest } from '../../common/search';
-export { LegacyApiCaller, SearchRequest, SearchResponse } from './es_client';
-
-export { SearchError, SearchStrategyProvider, getSearchErrorType } from './search_strategy';
+export {
+ SearchError,
+ FetchOptions,
+ SearchRequest,
+ SearchResponse,
+ getSearchErrorType,
+} from './fetch';
export {
ISearchSource,
@@ -59,5 +63,3 @@ export {
export { SearchInterceptor } from './search_interceptor';
export { RequestTimeoutError } from './request_timeout_error';
-
-export { FetchOptions } from './fetch';
diff --git a/src/plugins/data/public/search/fetch/call_client.test.ts b/src/plugins/data/public/search/legacy/call_client.test.ts
similarity index 91%
rename from src/plugins/data/public/search/fetch/call_client.test.ts
rename to src/plugins/data/public/search/legacy/call_client.test.ts
index 7a99b7c064515..f919187d46784 100644
--- a/src/plugins/data/public/search/fetch/call_client.test.ts
+++ b/src/plugins/data/public/search/legacy/call_client.test.ts
@@ -18,16 +18,17 @@
*/
import { callClient } from './call_client';
-import { handleResponse } from './handle_response';
-import { FetchHandlers } from './types';
-import { SearchStrategySearchParams, defaultSearchStrategy } from '../search_strategy';
+import { SearchStrategySearchParams } from './types';
+import { defaultSearchStrategy } from './default_search_strategy';
+import { FetchHandlers } from '../fetch';
+import { handleResponse } from '../fetch/handle_response';
const mockAbortFn = jest.fn();
-jest.mock('./handle_response', () => ({
+jest.mock('../fetch/handle_response', () => ({
handleResponse: jest.fn((request, response) => response),
}));
-jest.mock('../search_strategy', () => {
+jest.mock('./default_search_strategy', () => {
return {
defaultSearchStrategy: {
search: jest.fn(({ searchRequests }: SearchStrategySearchParams) => {
diff --git a/src/plugins/data/public/search/fetch/call_client.ts b/src/plugins/data/public/search/legacy/call_client.ts
similarity index 89%
rename from src/plugins/data/public/search/fetch/call_client.ts
rename to src/plugins/data/public/search/legacy/call_client.ts
index b3c4c682fa60c..c484c46aa4879 100644
--- a/src/plugins/data/public/search/fetch/call_client.ts
+++ b/src/plugins/data/public/search/legacy/call_client.ts
@@ -17,10 +17,9 @@
* under the License.
*/
-import { handleResponse } from './handle_response';
-import { FetchOptions, FetchHandlers } from './types';
-import { defaultSearchStrategy } from '../search_strategy';
-import { SearchRequest } from '..';
+import { FetchOptions, FetchHandlers, handleResponse } from '../fetch';
+import { defaultSearchStrategy } from './default_search_strategy';
+import { SearchRequest } from '../index';
export function callClient(
searchRequests: SearchRequest[],
diff --git a/src/plugins/data/public/search/search_strategy/default_search_strategy.test.ts b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts
similarity index 83%
rename from src/plugins/data/public/search/search_strategy/default_search_strategy.test.ts
rename to src/plugins/data/public/search/legacy/default_search_strategy.test.ts
index 210a0e5fd1ac7..835b02b3cd5c7 100644
--- a/src/plugins/data/public/search/search_strategy/default_search_strategy.test.ts
+++ b/src/plugins/data/public/search/legacy/default_search_strategy.test.ts
@@ -17,10 +17,10 @@
* under the License.
*/
-import { IUiSettingsClient } from '../../../../../core/public';
-import { SearchStrategySearchParams } from './types';
+import { IUiSettingsClient } from 'kibana/public';
import { defaultSearchStrategy } from './default_search_strategy';
import { searchStartMock } from '../mocks';
+import { SearchStrategySearchParams } from './types';
const { search } = defaultSearchStrategy;
@@ -38,12 +38,6 @@ const searchMockResponse: any = Promise.resolve([]);
searchMockResponse.abort = jest.fn();
const searchMock = jest.fn().mockReturnValue(searchMockResponse);
-const newSearchMockResponse: any = Promise.resolve([]);
-newSearchMockResponse.abort = jest.fn();
-const newSearchMock = jest.fn().mockReturnValue({
- toPromise: () => searchMockResponse,
-});
-
describe('defaultSearchStrategy', function() {
describe('search', function() {
let searchArgs: MockedKeys>;
@@ -58,7 +52,6 @@ describe('defaultSearchStrategy', function() {
const searchService = searchStartMock;
searchService.aggs.calculateAutoTimeExpression = jest.fn().mockReturnValue('1d');
- searchService.search = newSearchMock;
searchService.__LEGACY.esClient.search = searchMock;
searchService.__LEGACY.esClient.msearch = msearchMock;
@@ -112,18 +105,5 @@ describe('defaultSearchStrategy', function() {
search({ ...searchArgs, config }).abort();
expect(msearchMockResponse.abort).toHaveBeenCalled();
});
-
- test('should call new search service', () => {
- const config = getConfigStub();
- search({ ...searchArgs, config });
- expect(newSearchMock).toHaveBeenCalledTimes(1);
- });
-
- test('should properly abort with new search service', async () => {
- const abortSpy = jest.spyOn(AbortController.prototype, 'abort');
- const config = getConfigStub({});
- search({ ...searchArgs, config }).abort();
- expect(abortSpy).toHaveBeenCalled();
- });
});
});
diff --git a/src/plugins/data/public/search/search_strategy/default_search_strategy.ts b/src/plugins/data/public/search/legacy/default_search_strategy.ts
similarity index 64%
rename from src/plugins/data/public/search/search_strategy/default_search_strategy.ts
rename to src/plugins/data/public/search/legacy/default_search_strategy.ts
index 2bd88f51587a8..1552410f9090c 100644
--- a/src/plugins/data/public/search/search_strategy/default_search_strategy.ts
+++ b/src/plugins/data/public/search/legacy/default_search_strategy.ts
@@ -17,23 +17,19 @@
* under the License.
*/
+import { getPreference, getTimeout } from '../fetch';
+import { getMSearchParams } from './get_msearch_params';
import { SearchStrategyProvider, SearchStrategySearchParams } from './types';
-import { isDefault } from '../../index_patterns';
-import { getSearchParams, getMSearchParams, getPreference, getTimeout } from './get_search_params';
+// @deprecated
export const defaultSearchStrategy: SearchStrategyProvider = {
id: 'default',
search: params => {
- return params.config.get('courier:batchSearches') ? msearch(params) : search(params);
- },
-
- isViable: indexPattern => {
- return indexPattern && isDefault(indexPattern);
+ return msearch(params);
},
};
-// @deprecated
function msearch({
searchRequests,
searchService,
@@ -65,29 +61,3 @@ function msearch({
abort: searching.abort,
};
}
-
-function search({
- searchRequests,
- searchService,
- config,
- esShardTimeout,
-}: SearchStrategySearchParams) {
- const abortController = new AbortController();
- const searchParams = getSearchParams(config, esShardTimeout);
- const promises = searchRequests.map(({ index, indexType, body }) => {
- const params = {
- index: index.title || index,
- body,
- ...searchParams,
- };
- const { signal } = abortController;
- return searchService
- .search({ params, indexType }, { signal })
- .toPromise()
- .then(({ rawResponse }) => rawResponse);
- });
- return {
- searching: Promise.all(promises),
- abort: () => abortController.abort(),
- };
-}
diff --git a/src/plugins/data/public/search/es_client/get_es_client.ts b/src/plugins/data/public/search/legacy/es_client/get_es_client.ts
similarity index 100%
rename from src/plugins/data/public/search/es_client/get_es_client.ts
rename to src/plugins/data/public/search/legacy/es_client/get_es_client.ts
diff --git a/src/plugins/data/public/search/es_client/index.ts b/src/plugins/data/public/search/legacy/es_client/index.ts
similarity index 91%
rename from src/plugins/data/public/search/es_client/index.ts
rename to src/plugins/data/public/search/legacy/es_client/index.ts
index b1e0ce3116824..78ac83af642d8 100644
--- a/src/plugins/data/public/search/es_client/index.ts
+++ b/src/plugins/data/public/search/legacy/es_client/index.ts
@@ -18,4 +18,4 @@
*/
export { getEsClient } from './get_es_client';
-export { SearchRequest, SearchResponse, LegacyApiCaller } from './types';
+export { LegacyApiCaller } from './types';
diff --git a/src/plugins/data/public/search/es_client/types.ts b/src/plugins/data/public/search/legacy/es_client/types.ts
similarity index 94%
rename from src/plugins/data/public/search/es_client/types.ts
rename to src/plugins/data/public/search/legacy/es_client/types.ts
index 3ca0513a14238..7a56b9b0cb00a 100644
--- a/src/plugins/data/public/search/es_client/types.ts
+++ b/src/plugins/data/public/search/legacy/es_client/types.ts
@@ -17,8 +17,7 @@
* under the License.
*/
-export type SearchRequest = any;
-export type SearchResponse = any;
+import { SearchRequest, SearchResponse } from '../../fetch';
export interface LegacyApiCaller {
search: (searchRequest: SearchRequest) => LegacyApiCallerResponse;
diff --git a/src/plugins/data/public/search/fetch/fetch_soon.test.ts b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
similarity index 96%
rename from src/plugins/data/public/search/fetch/fetch_soon.test.ts
rename to src/plugins/data/public/search/legacy/fetch_soon.test.ts
index a8d593c8501f6..b2e17798ccc9f 100644
--- a/src/plugins/data/public/search/fetch/fetch_soon.test.ts
+++ b/src/plugins/data/public/search/legacy/fetch_soon.test.ts
@@ -19,9 +19,9 @@
import { fetchSoon } from './fetch_soon';
import { callClient } from './call_client';
-import { IUiSettingsClient } from '../../../../../core/public';
-import { FetchHandlers, FetchOptions } from './types';
-import { SearchRequest, SearchResponse } from '..';
+import { IUiSettingsClient } from 'kibana/public';
+import { FetchHandlers, FetchOptions } from '../fetch/types';
+import { SearchRequest, SearchResponse } from '../index';
function getConfigStub(config: any = {}) {
return {
diff --git a/src/plugins/data/public/search/fetch/fetch_soon.ts b/src/plugins/data/public/search/legacy/fetch_soon.ts
similarity index 95%
rename from src/plugins/data/public/search/fetch/fetch_soon.ts
rename to src/plugins/data/public/search/legacy/fetch_soon.ts
index b1405747426ee..18fa410a5bef0 100644
--- a/src/plugins/data/public/search/fetch/fetch_soon.ts
+++ b/src/plugins/data/public/search/legacy/fetch_soon.ts
@@ -18,8 +18,8 @@
*/
import { callClient } from './call_client';
-import { FetchHandlers, FetchOptions } from './types';
-import { SearchRequest, SearchResponse } from '..';
+import { FetchHandlers, FetchOptions } from '../fetch/types';
+import { SearchRequest, SearchResponse } from '../index';
/**
* This function introduces a slight delay in the request process to allow multiple requests to queue
diff --git a/src/plugins/data/public/search/search_strategy/get_search_params.test.ts b/src/plugins/data/public/search/legacy/get_msearch_params.test.ts
similarity index 60%
rename from src/plugins/data/public/search/search_strategy/get_search_params.test.ts
rename to src/plugins/data/public/search/legacy/get_msearch_params.test.ts
index 76f3105d7f942..9f16d5b408178 100644
--- a/src/plugins/data/public/search/search_strategy/get_search_params.test.ts
+++ b/src/plugins/data/public/search/legacy/get_msearch_params.test.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { getMSearchParams, getSearchParams } from './get_search_params';
+import { getMSearchParams } from './get_msearch_params';
import { IUiSettingsClient } from '../../../../../core/public';
function getConfigStub(config: any = {}) {
@@ -64,46 +64,3 @@ describe('getMSearchParams', () => {
expect(msearchParams.hasOwnProperty('timeout')).toBe(false);
});
});
-
-describe('getSearchParams', () => {
- test('includes rest_total_hits_as_int', () => {
- const config = getConfigStub();
- const searchParams = getSearchParams(config);
- expect(searchParams.rest_total_hits_as_int).toBe(true);
- });
-
- test('includes ignore_unavailable', () => {
- const config = getConfigStub();
- const searchParams = getSearchParams(config);
- expect(searchParams.ignore_unavailable).toBe(true);
- });
-
- test('includes ignore_throttled according to search:includeFrozen', () => {
- let config = getConfigStub({ 'search:includeFrozen': true });
- let searchParams = getSearchParams(config);
- expect(searchParams.ignore_throttled).toBe(false);
-
- config = getConfigStub({ 'search:includeFrozen': false });
- searchParams = getSearchParams(config);
- expect(searchParams.ignore_throttled).toBe(true);
- });
-
- test('includes max_concurrent_shard_requests according to courier:maxConcurrentShardRequests', () => {
- let config = getConfigStub({ 'courier:maxConcurrentShardRequests': 0 });
- let searchParams = getSearchParams(config);
- expect(searchParams.max_concurrent_shard_requests).toBe(undefined);
-
- config = getConfigStub({ 'courier:maxConcurrentShardRequests': 5 });
- searchParams = getSearchParams(config);
- expect(searchParams.max_concurrent_shard_requests).toBe(5);
- });
-
- test('includes timeout according to esShardTimeout if greater than 0', () => {
- const config = getConfigStub();
- let searchParams = getSearchParams(config, 0);
- expect(searchParams.timeout).toBe(undefined);
-
- searchParams = getSearchParams(config, 100);
- expect(searchParams.timeout).toBe('100ms');
- });
-});
diff --git a/src/plugins/data/public/search/legacy/get_msearch_params.ts b/src/plugins/data/public/search/legacy/get_msearch_params.ts
new file mode 100644
index 0000000000000..48d13903c972f
--- /dev/null
+++ b/src/plugins/data/public/search/legacy/get_msearch_params.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.
+ */
+
+import { IUiSettingsClient } from 'kibana/public';
+import { getIgnoreThrottled, getMaxConcurrentShardRequests } from '../fetch';
+
+export function getMSearchParams(config: IUiSettingsClient) {
+ return {
+ rest_total_hits_as_int: true,
+ ignore_throttled: getIgnoreThrottled(config),
+ max_concurrent_shard_requests: getMaxConcurrentShardRequests(config),
+ };
+}
diff --git a/src/plugins/data/public/search/legacy/index.ts b/src/plugins/data/public/search/legacy/index.ts
new file mode 100644
index 0000000000000..e2ae72824f3f4
--- /dev/null
+++ b/src/plugins/data/public/search/legacy/index.ts
@@ -0,0 +1,21 @@
+/*
+ * 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 { fetchSoon } from './fetch_soon';
+export { getEsClient, LegacyApiCaller } from './es_client';
diff --git a/src/plugins/data/public/search/search_strategy/types.ts b/src/plugins/data/public/search/legacy/types.ts
similarity index 89%
rename from src/plugins/data/public/search/search_strategy/types.ts
rename to src/plugins/data/public/search/legacy/types.ts
index 764370d8ff649..3812cec7a2aa2 100644
--- a/src/plugins/data/public/search/search_strategy/types.ts
+++ b/src/plugins/data/public/search/legacy/types.ts
@@ -17,21 +17,20 @@
* under the License.
*/
-import { IndexPattern } from '../..';
-import { FetchHandlers } from '../fetch/types';
+import { FetchHandlers } from '../fetch';
import { SearchRequest, SearchResponse } from '..';
+export interface SearchStrategySearchParams extends FetchHandlers {
+ searchRequests: SearchRequest[];
+}
+
+// @deprecated
export interface SearchStrategyProvider {
id: string;
search: (params: SearchStrategySearchParams) => SearchStrategyResponse;
- isViable: (indexPattern: IndexPattern) => boolean;
}
export interface SearchStrategyResponse {
searching: Promise;
abort: () => void;
}
-
-export interface SearchStrategySearchParams extends FetchHandlers {
- searchRequests: SearchRequest[];
-}
diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts
index a539736991adb..916278a96659b 100644
--- a/src/plugins/data/public/search/search_service.ts
+++ b/src/plugins/data/public/search/search_service.ts
@@ -22,12 +22,12 @@ import { Plugin, CoreSetup, CoreStart, PackageInfo } from '../../../../core/publ
import { SYNC_SEARCH_STRATEGY, syncSearchStrategyProvider } from './sync_search_strategy';
import { ISearchSetup, ISearchStart, TSearchStrategyProvider, TSearchStrategiesMap } from './types';
import { TStrategyTypes } from './strategy_types';
-import { getEsClient, LegacyApiCaller } from './es_client';
+import { getEsClient, LegacyApiCaller } from './legacy';
import { ES_SEARCH_STRATEGY, DEFAULT_SEARCH_STRATEGY } from '../../common/search';
-import { esSearchStrategyProvider } from './es_search/es_search_strategy';
+import { esSearchStrategyProvider } from './es_search';
import { IndexPatternsContract } from '../index_patterns/index_patterns';
import { createSearchSource } from './search_source';
-import { QuerySetup } from '../query/query_service';
+import { QuerySetup } from '../query';
import { GetInternalStartServicesFn } from '../types';
import { SearchInterceptor } from './search_interceptor';
import {
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 6bad093d31402..6e878844664ad 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
@@ -20,8 +20,26 @@
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';
-jest.mock('../fetch', () => ({
+// Setup search service mock
+searchStartMock.search = jest.fn(() => {
+ return new Observable(subscriber => {
+ setTimeout(() => {
+ subscriber.next({
+ rawResponse: '',
+ });
+ subscriber.complete();
+ }, 100);
+ });
+}) as any;
+setSearchService(searchStartMock);
+
+jest.mock('../legacy', () => ({
fetchSoon: jest.fn().mockResolvedValue({}),
}));
@@ -44,8 +62,11 @@ const indexPattern2 = ({
} as unknown) as IndexPattern;
describe('SearchSource', function() {
+ let uiSettingsMock: jest.Mocked;
beforeEach(() => {
- mockDataServices();
+ const { core } = mockDataServices();
+ uiSettingsMock = core.uiSettings;
+ jest.clearAllMocks();
});
describe('#setField()', function() {
@@ -151,6 +172,36 @@ describe('SearchSource', function() {
});
});
+ describe('#legacy fetch()', () => {
+ beforeEach(() => {
+ uiSettingsMock.get.mockImplementation(() => {
+ return true; // batchSearches = true
+ });
+ });
+
+ afterEach(() => {
+ uiSettingsMock.get.mockImplementation(() => {
+ return false; // batchSearches = false
+ });
+ });
+
+ it('should call msearch', async () => {
+ const searchSource = new SearchSource({ index: indexPattern });
+ const options = {};
+ await searchSource.fetch(options);
+ expect(fetchSoon).toBeCalledTimes(1);
+ });
+ });
+
+ describe('#search service fetch()', () => {
+ it('should call msearch', async () => {
+ const searchSource = new SearchSource({ index: indexPattern });
+ const options = {};
+ await searchSource.fetch(options);
+ expect(searchStartMock.search).toBeCalledTimes(1);
+ });
+ });
+
describe('#serialize', function() {
it('should reference index patterns', () => {
const indexPattern123 = { id: '123' } as IndexPattern;
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 c70db7bb82ef7..9d2bb889953cf 100644
--- a/src/plugins/data/public/search/search_source/search_source.ts
+++ b/src/plugins/data/public/search/search_source/search_source.ts
@@ -70,17 +70,19 @@
*/
import _ from 'lodash';
+import { map } from 'rxjs/operators';
import { 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 { SearchSourceOptions, SearchSourceFields } from './types';
-import { fetchSoon, FetchOptions, RequestFailure } from '../fetch';
+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';
export type ISearchSource = Pick;
@@ -185,18 +187,29 @@ export class SearchSource {
}
/**
- * Fetch this source and reject the returned Promise on error
- *
- * @async
+ * Run a search using the search service
+ * @return {Observable>}
*/
- async fetch(options: FetchOptions = {}) {
- await this.requestIsStarting(options);
-
- const searchRequest = await this.flatten();
- this.history = [searchRequest];
+ private fetch$(searchRequest: SearchRequest, signal?: AbortSignal) {
+ const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number;
+ const searchParams = getSearchParams(getUiSettings(), 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)));
+ }
+ /**
+ * Run a search using the search service
+ * @return {Promise>}
+ */
+ private async legacyFetch(searchRequest: SearchRequest, options: FetchOptions) {
const esShardTimeout = getInjectedMetadata().getInjectedVar('esShardTimeout') as number;
- const response = await fetchSoon(
+ return await fetchSoon(
searchRequest,
{
...(this.searchStrategyId && { searchStrategyId: this.searchStrategyId }),
@@ -208,6 +221,24 @@ export class SearchSource {
esShardTimeout,
}
);
+ }
+ /**
+ * Fetch this source and reject the returned Promise on error
+ *
+ * @async
+ */
+ async fetch(options: FetchOptions = {}) {
+ await this.requestIsStarting(options);
+
+ const searchRequest = await this.flatten();
+ this.history = [searchRequest];
+
+ let response;
+ if (getUiSettings().get('courier:batchSearches')) {
+ response = await this.legacyFetch(searchRequest, options);
+ } else {
+ response = this.fetch$(searchRequest, options.abortSignal).toPromise();
+ }
if (response.error) {
throw new RequestFailure(null, response);
@@ -246,7 +277,6 @@ export class SearchSource {
/**
* Called by requests of this search source when they are started
- * @param {Courier.Request} request
* @param options
* @return {Promise}
*/
@@ -430,7 +460,6 @@ export class SearchSource {
* and `kibanaSavedObjectMeta.searchSourceJSON.filter[].meta.index`.
*
* Using `createSearchSource`, the instance can be re-created.
- * @param searchSource The search source to serialize
* @public */
public serialize() {
const references: SavedObjectReference[] = [];
diff --git a/src/plugins/data/public/search/search_strategy/no_op_search_strategy.ts b/src/plugins/data/public/search/search_strategy/no_op_search_strategy.ts
deleted file mode 100644
index dc7331e614a0e..0000000000000
--- a/src/plugins/data/public/search/search_strategy/no_op_search_strategy.ts
+++ /dev/null
@@ -1,55 +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 { SearchError } from './search_error';
-import { SearchStrategyProvider } from './types';
-
-export const noOpSearchStrategy: SearchStrategyProvider = {
- id: 'noOp',
-
- search: () => {
- const searchError = new SearchError({
- status: '418', // "I'm a teapot" error
- title: i18n.translate(
- 'data.search.searchSource.noSearchStrategyRegisteredErrorMessageTitle',
- {
- defaultMessage: 'No search strategy registered',
- }
- ),
- message: i18n.translate(
- 'data.search.searchSource.noSearchStrategyRegisteredErrorMessageDescription',
- {
- defaultMessage: `Couldn't find a search strategy for the search request`,
- }
- ),
- type: 'NO_OP_SEARCH_STRATEGY',
- path: '',
- });
-
- return {
- searching: Promise.reject(searchError),
- abort: () => {},
- };
- },
-
- isViable: () => {
- return true;
- },
-};
diff --git a/src/plugins/data/public/search/types.ts b/src/plugins/data/public/search/types.ts
index ba6e44f47b75e..2122e4e82ec1d 100644
--- a/src/plugins/data/public/search/types.ts
+++ b/src/plugins/data/public/search/types.ts
@@ -22,7 +22,7 @@ import { createSearchSource } from './search_source';
import { SearchAggsSetup, SearchAggsStart, SearchAggsStartLegacy } from './aggs';
import { ISearch, ISearchGeneric } from './i_search';
import { TStrategyTypes } from './strategy_types';
-import { LegacyApiCaller } from './es_client';
+import { LegacyApiCaller } from './legacy/es_client';
import { SearchInterceptor } from './search_interceptor';
export interface ISearchContext {
@@ -38,15 +38,6 @@ export interface ISearchStrategy {
search: ISearch;
}
-/**
- * Search strategy provider creates an instance of a search strategy with the request
- * handler context bound to it. This way every search strategy can use
- * whatever information they require from the request context.
- */
-export type TSearchStrategyProviderEnhanced = (
- search: ISearchGeneric
-) => Promise>;
-
export type TSearchStrategiesMap = {
[K in TStrategyTypes]?: TSearchStrategyProvider;
};
diff --git a/src/plugins/data/server/saved_objects/index_patterns.ts b/src/plugins/data/server/saved_objects/index_patterns.ts
index 9838071eee5a4..497dbb7d6f630 100644
--- a/src/plugins/data/server/saved_objects/index_patterns.ts
+++ b/src/plugins/data/server/saved_objects/index_patterns.ts
@@ -23,7 +23,7 @@ import { indexPatternSavedObjectTypeMigrations } from './index_pattern_migration
export const indexPatternSavedObjectType: SavedObjectsType = {
name: 'index-pattern',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'indexPatternApp',
defaultSearchField: 'title',
diff --git a/src/plugins/data/server/saved_objects/query.ts b/src/plugins/data/server/saved_objects/query.ts
index ff0a6cfde8113..015cc7c6cb134 100644
--- a/src/plugins/data/server/saved_objects/query.ts
+++ b/src/plugins/data/server/saved_objects/query.ts
@@ -22,7 +22,7 @@ import { SavedObjectsType } from 'kibana/server';
export const querySavedObjectType: SavedObjectsType = {
name: 'query',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'search',
defaultSearchField: 'title',
diff --git a/src/plugins/data/server/saved_objects/search.ts b/src/plugins/data/server/saved_objects/search.ts
index 8b30ff7d08201..54de35a90ae02 100644
--- a/src/plugins/data/server/saved_objects/search.ts
+++ b/src/plugins/data/server/saved_objects/search.ts
@@ -23,7 +23,7 @@ import { searchSavedObjectTypeMigrations } from './search_migrations';
export const searchSavedObjectType: SavedObjectsType = {
name: 'search',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'discoverApp',
defaultSearchField: 'title',
diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx
new file mode 100644
index 0000000000000..68f1cf2045efb
--- /dev/null
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/authorization_provider.tsx
@@ -0,0 +1,73 @@
+/*
+ * 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 { HttpSetup } from 'kibana/public';
+import React, { createContext, useContext } from 'react';
+
+import { useRequest } from '../../../public';
+
+import { Error as CustomError } from './section_error';
+
+import { Privileges } from '../types';
+
+interface Authorization {
+ isLoading: boolean;
+ apiError: CustomError | null;
+ privileges: Privileges;
+}
+
+const initialValue: Authorization = {
+ isLoading: true,
+ apiError: null,
+ privileges: {
+ hasAllPrivileges: true,
+ missingPrivileges: {},
+ },
+};
+
+export const AuthorizationContext = createContext(initialValue);
+
+export const useAuthorizationContext = () => {
+ const ctx = useContext(AuthorizationContext);
+ if (!ctx) {
+ throw new Error('AuthorizationContext can only be used inside of AuthorizationProvider!');
+ }
+ return ctx;
+};
+
+interface Props {
+ privilegesEndpoint: string;
+ children: React.ReactNode;
+ httpClient: HttpSetup;
+}
+
+export const AuthorizationProvider = ({ privilegesEndpoint, httpClient, children }: Props) => {
+ const { isLoading, error, data: privilegesData } = useRequest(httpClient, {
+ path: privilegesEndpoint,
+ method: 'get',
+ });
+
+ const value = {
+ isLoading,
+ privileges: isLoading ? { hasAllPrivileges: true, missingPrivileges: {} } : privilegesData,
+ apiError: error ? error : null,
+ } as Authorization;
+
+ return {children};
+};
diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/index.ts
new file mode 100644
index 0000000000000..71be3cc6152ca
--- /dev/null
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/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.
+ */
+
+export {
+ AuthorizationProvider,
+ AuthorizationContext,
+ useAuthorizationContext,
+} from './authorization_provider';
+
+export { WithPrivileges } from './with_privileges';
+
+export { NotAuthorizedSection } from './not_authorized_section';
+
+export { Error, SectionError } from './section_error';
diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/not_authorized_section.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/not_authorized_section.tsx
new file mode 100644
index 0000000000000..c35f674ef9ec4
--- /dev/null
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/not_authorized_section.tsx
@@ -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 React from 'react';
+import { EuiEmptyPrompt } from '@elastic/eui';
+
+interface Props {
+ title: React.ReactNode;
+ message: React.ReactNode | string;
+}
+
+export const NotAuthorizedSection = ({ title, message }: Props) => (
+ {title}} body={{message}
} />
+);
diff --git a/x-pack/plugins/snapshot_restore/public/application/components/section_error.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx
similarity index 54%
rename from x-pack/plugins/snapshot_restore/public/application/components/section_error.tsx
rename to src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx
index bd9e48796779e..3d56309adae97 100644
--- a/x-pack/plugins/snapshot_restore/public/application/components/section_error.tsx
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/section_error.tsx
@@ -1,7 +1,20 @@
/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
+ * 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 { EuiCallOut, EuiSpacer } from '@elastic/eui';
diff --git a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/with_privileges.tsx b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx
similarity index 70%
rename from x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/with_privileges.tsx
rename to src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx
index 223a2882c3cab..8f4b2b976d141 100644
--- a/x-pack/plugins/snapshot_restore/public/application/lib/authorization/components/with_privileges.tsx
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/components/with_privileges.tsx
@@ -1,13 +1,25 @@
/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
+ * 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 { useContext } from 'react';
+import { MissingPrivileges } from '../types';
-import { MissingPrivileges } from '../../../../../common/types';
-import { AuthorizationContext } from './authorization_provider';
+import { useAuthorizationContext } from './authorization_provider';
interface Props {
/**
@@ -29,7 +41,7 @@ const toArray = (value: string | string[]): string[] =>
Array.isArray(value) ? (value as string[]) : ([value] as string[]);
export const WithPrivileges = ({ privileges: requiredPrivileges, children }: Props) => {
- const { isLoading, privileges } = useContext(AuthorizationContext);
+ const { isLoading, privileges } = useAuthorizationContext();
const privilegesToArray: Privilege[] = toArray(requiredPrivileges).map(p => {
const [section, privilege] = p.split('.');
diff --git a/src/plugins/data/public/search/search_strategy/index.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts
similarity index 77%
rename from src/plugins/data/public/search/search_strategy/index.ts
rename to src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts
index e3de2ea46e3ec..ad89052b3bb54 100644
--- a/src/plugins/data/public/search/search_strategy/index.ts
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/index.ts
@@ -17,8 +17,14 @@
* under the License.
*/
-export { SearchError, getSearchErrorType } from './search_error';
+export {
+ WithPrivileges,
+ NotAuthorizedSection,
+ AuthorizationProvider,
+ AuthorizationContext,
+ SectionError,
+ Error,
+ useAuthorizationContext,
+} from './components';
-export { SearchStrategyProvider, SearchStrategySearchParams } from './types';
-
-export { defaultSearchStrategy } from './default_search_strategy';
+export { Privileges, MissingPrivileges } from './types';
diff --git a/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.ts
new file mode 100644
index 0000000000000..cdc2052122688
--- /dev/null
+++ b/src/plugins/es_ui_shared/__packages_do_not_import__/authorization/types.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 interface MissingPrivileges {
+ [key: string]: string[] | undefined;
+}
+
+export interface Privileges {
+ hasAllPrivileges: boolean;
+ missingPrivileges: MissingPrivileges;
+}
diff --git a/src/plugins/es_ui_shared/public/authorization/index.ts b/src/plugins/es_ui_shared/public/authorization/index.ts
new file mode 100644
index 0000000000000..3a02c0d2694f3
--- /dev/null
+++ b/src/plugins/es_ui_shared/public/authorization/index.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 * from '../../__packages_do_not_import__/authorization';
diff --git a/src/plugins/es_ui_shared/public/index.ts b/src/plugins/es_ui_shared/public/index.ts
index a0371bf351193..7e5510d7c9c65 100644
--- a/src/plugins/es_ui_shared/public/index.ts
+++ b/src/plugins/es_ui_shared/public/index.ts
@@ -47,6 +47,18 @@ export {
expandLiteralStrings,
} from './console_lang';
+export {
+ AuthorizationContext,
+ AuthorizationProvider,
+ NotAuthorizedSection,
+ WithPrivileges,
+ Privileges,
+ MissingPrivileges,
+ SectionError,
+ Error,
+ useAuthorizationContext,
+} from './authorization';
+
/** dummy plugin, we just want esUiShared to have its own bundle */
export function plugin() {
return new (class EsUiSharedPlugin {
diff --git a/src/plugins/expressions/kibana.json b/src/plugins/expressions/kibana.json
index cba693dd4bc20..5d2112103e94d 100644
--- a/src/plugins/expressions/kibana.json
+++ b/src/plugins/expressions/kibana.json
@@ -4,7 +4,6 @@
"server": true,
"ui": true,
"requiredPlugins": [
- "bfetch",
- "inspector"
+ "bfetch"
]
}
diff --git a/src/plugins/expressions/public/loader.ts b/src/plugins/expressions/public/loader.ts
index fbe2f37c648d6..418ff6fdf8614 100644
--- a/src/plugins/expressions/public/loader.ts
+++ b/src/plugins/expressions/public/loader.ts
@@ -19,13 +19,14 @@
import { BehaviorSubject, Observable, Subject } from 'rxjs';
import { filter, map } from 'rxjs/operators';
-import { Adapters, InspectorSession } from '../../inspector/public';
-import { ExpressionRenderHandler } from './render';
+import { Adapters } from '../../inspector/public';
import { IExpressionLoaderParams } from './types';
import { ExpressionAstExpression } from '../common';
-import { getInspector, getExpressionsService } from './services';
import { ExecutionContract } from '../common/execution/execution_contract';
+import { ExpressionRenderHandler } from './render';
+import { getExpressionsService } from './services';
+
type Data = any;
export class ExpressionLoader {
@@ -120,15 +121,6 @@ export class ExpressionLoader {
return this.renderHandler.getElement();
}
- openInspector(title: string): InspectorSession | undefined {
- const inspector = this.inspect();
- if (inspector) {
- return getInspector().open(inspector, {
- title,
- });
- }
- }
-
inspect(): Adapters | undefined {
return this.execution ? (this.execution.inspect() as Adapters) : undefined;
}
diff --git a/src/plugins/expressions/public/mocks.tsx b/src/plugins/expressions/public/mocks.tsx
index cb7089f814643..b8f2f693e9c77 100644
--- a/src/plugins/expressions/public/mocks.tsx
+++ b/src/plugins/expressions/public/mocks.tsx
@@ -22,7 +22,6 @@ import { ExpressionsSetup, ExpressionsStart, plugin as pluginInitializer } from
/* eslint-disable */
import { coreMock } from '../../../core/public/mocks';
-import { inspectorPluginMock } from '../../inspector/public/mocks';
import { bfetchPluginMock } from '../../bfetch/public/mocks';
/* eslint-enable */
@@ -89,7 +88,6 @@ const createPlugin = async () => {
const plugin = pluginInitializer(pluginInitializerContext);
const setup = await plugin.setup(coreSetup, {
bfetch: bfetchPluginMock.createSetupContract(),
- inspector: inspectorPluginMock.createSetupContract(),
});
return {
@@ -101,7 +99,6 @@ const createPlugin = async () => {
doStart: async () =>
await plugin.start(coreStart, {
bfetch: bfetchPluginMock.createStartContract(),
- inspector: inspectorPluginMock.createStartContract(),
}),
};
};
diff --git a/src/plugins/expressions/public/plugin.ts b/src/plugins/expressions/public/plugin.ts
index 7c0de271b7706..720c3b701d504 100644
--- a/src/plugins/expressions/public/plugin.ts
+++ b/src/plugins/expressions/public/plugin.ts
@@ -29,11 +29,9 @@ import {
ExpressionsServiceStart,
ExecutionContext,
} from '../common';
-import { Setup as InspectorSetup, Start as InspectorStart } from '../../inspector/public';
import { BfetchPublicSetup, BfetchPublicStart } from '../../bfetch/public';
import {
setCoreStart,
- setInspector,
setInterpreter,
setRenderersRegistry,
setNotifications,
@@ -45,12 +43,10 @@ import { render, ExpressionRenderHandler } from './render';
export interface ExpressionsSetupDeps {
bfetch: BfetchPublicSetup;
- inspector: InspectorSetup;
}
export interface ExpressionsStartDeps {
bfetch: BfetchPublicStart;
- inspector: InspectorStart;
}
export interface ExpressionsSetup extends ExpressionsServiceSetup {
@@ -120,7 +116,7 @@ export class ExpressionsPublicPlugin
});
}
- public setup(core: CoreSetup, { inspector, bfetch }: ExpressionsSetupDeps): ExpressionsSetup {
+ public setup(core: CoreSetup, { bfetch }: ExpressionsSetupDeps): ExpressionsSetup {
this.configureExecutor(core);
const { expressions } = this;
@@ -180,9 +176,8 @@ export class ExpressionsPublicPlugin
return Object.freeze(setup);
}
- public start(core: CoreStart, { inspector, bfetch }: ExpressionsStartDeps): ExpressionsStart {
+ public start(core: CoreStart, { bfetch }: ExpressionsStartDeps): ExpressionsStart {
setCoreStart(core);
- setInspector(inspector);
setNotifications(core.notifications);
const { expressions } = this;
diff --git a/src/plugins/expressions/public/react_expression_renderer.tsx b/src/plugins/expressions/public/react_expression_renderer.tsx
index 242a49c6d6639..2c99f173c9f33 100644
--- a/src/plugins/expressions/public/react_expression_renderer.tsx
+++ b/src/plugins/expressions/public/react_expression_renderer.tsx
@@ -17,8 +17,7 @@
* under the License.
*/
-import { useRef, useEffect, useState, useLayoutEffect } from 'react';
-import React from 'react';
+import React, { useRef, useEffect, useState, useLayoutEffect } from 'react';
import classNames from 'classnames';
import { Subscription } from 'rxjs';
import { filter } from 'rxjs/operators';
diff --git a/src/plugins/expressions/public/render.ts b/src/plugins/expressions/public/render.ts
index ad4d16bcd1323..4aaf0da60fc60 100644
--- a/src/plugins/expressions/public/render.ts
+++ b/src/plugins/expressions/public/render.ts
@@ -21,10 +21,11 @@ import * as Rx from 'rxjs';
import { Observable } from 'rxjs';
import { filter } from 'rxjs/operators';
import { RenderError, RenderErrorHandlerFnType, IExpressionLoaderParams } from './types';
-import { getRenderersRegistry } from './services';
import { renderErrorHandler as defaultRenderErrorHandler } from './render_error_handler';
import { IInterpreterRenderHandlers, ExpressionAstExpression } from '../common';
+import { getRenderersRegistry } from './services';
+
export type IExpressionRendererExtraHandlers = Record;
export interface ExpressionRenderHandlerParams {
diff --git a/src/plugins/expressions/public/services.ts b/src/plugins/expressions/public/services.ts
index a203e87414571..016456c956666 100644
--- a/src/plugins/expressions/public/services.ts
+++ b/src/plugins/expressions/public/services.ts
@@ -20,14 +20,11 @@
import { NotificationsStart } from 'kibana/public';
import { createKibanaUtilsCore, createGetterSetter } from '../../kibana_utils/public';
import { ExpressionInterpreter } from './types';
-import { Start as IInspector } from '../../inspector/public';
import { ExpressionsSetup } from './plugin';
import { ExpressionsService } from '../common';
export const { getCoreStart, setCoreStart } = createKibanaUtilsCore();
-export const [getInspector, setInspector] = createGetterSetter('Inspector');
-
export const [getInterpreter, setInterpreter] = createGetterSetter(
'Interpreter'
);
diff --git a/src/plugins/kibana_legacy/public/angular/angular_config.tsx b/src/plugins/kibana_legacy/public/angular/angular_config.tsx
index 71cd57ef2d72e..295cf27688c80 100644
--- a/src/plugins/kibana_legacy/public/angular/angular_config.tsx
+++ b/src/plugins/kibana_legacy/public/angular/angular_config.tsx
@@ -92,9 +92,9 @@ export const configureAppAngularModule = (
) => {
const core = 'core' in newPlatform ? newPlatform.core : newPlatform;
const packageInfo =
- 'injectedMetadata' in newPlatform
- ? newPlatform.injectedMetadata.getLegacyMetadata()
- : newPlatform.env.packageInfo;
+ 'env' in newPlatform
+ ? newPlatform.env.packageInfo
+ : newPlatform.injectedMetadata.getLegacyMetadata();
if ('injectedMetadata' in newPlatform) {
forOwn(newPlatform.injectedMetadata.getInjectedVars(), (val, name) => {
diff --git a/src/plugins/kibana_legacy/public/mocks.ts b/src/plugins/kibana_legacy/public/mocks.ts
index 8e9a05b186191..2fdd0d8b4be59 100644
--- a/src/plugins/kibana_legacy/public/mocks.ts
+++ b/src/plugins/kibana_legacy/public/mocks.ts
@@ -25,6 +25,7 @@ export type Start = jest.Mocked>;
const createSetupContract = (): Setup => ({
forwardApp: jest.fn(),
+ registerLegacyAppAlias: jest.fn(),
registerLegacyApp: jest.fn(),
config: {
defaultAppId: 'home',
@@ -37,6 +38,7 @@ const createSetupContract = (): Setup => ({
const createStartContract = (): Start => ({
getApps: jest.fn(),
+ getLegacyAppAliases: jest.fn(),
getForwards: jest.fn(),
config: {
defaultAppId: 'home',
diff --git a/src/plugins/kibana_legacy/public/plugin.ts b/src/plugins/kibana_legacy/public/plugin.ts
index 2ad620f355848..831fc3f0d4a71 100644
--- a/src/plugins/kibana_legacy/public/plugin.ts
+++ b/src/plugins/kibana_legacy/public/plugin.ts
@@ -28,12 +28,18 @@ import { Observable } from 'rxjs';
import { ConfigSchema } from '../config';
import { getDashboardConfig } from './dashboard_config';
-interface ForwardDefinition {
+interface LegacyAppAliasDefinition {
legacyAppId: string;
newAppId: string;
keepPrefix: boolean;
}
+interface ForwardDefinition {
+ legacyAppId: string;
+ newAppId: string;
+ rewritePath: (legacyPath: string) => string;
+}
+
export type AngularRenderedAppUpdater = (
app: AppBase
) => Partial | undefined;
@@ -54,7 +60,8 @@ export interface AngularRenderedApp extends App {
export class KibanaLegacyPlugin {
private apps: AngularRenderedApp[] = [];
- private forwards: ForwardDefinition[] = [];
+ private legacyAppAliases: LegacyAppAliasDefinition[] = [];
+ private forwardDefinitions: ForwardDefinition[] = [];
constructor(private readonly initializerContext: PluginInitializerContext) {}
@@ -94,17 +101,55 @@ export class KibanaLegacyPlugin {
* renaming or nesting plugins. For route changes after the prefix, please
* use the routing mechanism of your app.
*
+ * This method just redirects URLs within the legacy `kibana` app.
+ *
* @param legacyAppId The name of the old app to forward URLs from
* @param newAppId The name of the new app that handles the URLs now
* @param options Whether the prefix of the old app is kept to nest the legacy
* path into the new path
*/
- forwardApp: (
+ registerLegacyAppAlias: (
legacyAppId: string,
newAppId: string,
options: { keepPrefix: boolean } = { keepPrefix: false }
) => {
- this.forwards.push({ legacyAppId, newAppId, ...options });
+ this.legacyAppAliases.push({ legacyAppId, newAppId, ...options });
+ },
+
+ /**
+ * Forwards URLs within the legacy `kibana` app to a new platform application.
+ *
+ * @param legacyAppId The name of the old app to forward URLs from
+ * @param newAppId The name of the new app that handles the URLs now
+ * @param rewritePath Function to rewrite the legacy sub path of the app to the new path in the core app
+ * path into the new path
+ *
+ * Example usage:
+ * ```
+ * kibanaLegacy.forwardApp(
+ * 'old',
+ * 'new',
+ * path => {
+ * const [, id] = /old/item\/(.*)$/.exec(path) || [];
+ * if (!id) {
+ * return '#/home';
+ * }
+ * return '#/items/${id}';
+ * }
+ * );
+ * ```
+ * This will cause the following redirects:
+ *
+ * * app/kibana#/old/ -> app/new#/home
+ * * app/kibana#/old/item/123 -> app/new#/items/123
+ *
+ */
+ forwardApp: (
+ legacyAppId: string,
+ newAppId: string,
+ rewritePath: (legacyPath: string) => string
+ ) => {
+ this.forwardDefinitions.push({ legacyAppId, newAppId, rewritePath });
},
/**
@@ -132,7 +177,12 @@ export class KibanaLegacyPlugin {
* @deprecated
* Just exported for wiring up with legacy platform, should not be used.
*/
- getForwards: () => this.forwards,
+ getLegacyAppAliases: () => this.legacyAppAliases,
+ /**
+ * @deprecated
+ * Just exported for wiring up with legacy platform, should not be used.
+ */
+ getForwards: () => this.forwardDefinitions,
config: this.initializerContext.config.get(),
dashboardConfig: getDashboardConfig(!application.capabilities.dashboard.showWriteControls),
};
diff --git a/src/plugins/kibana_react/public/index.ts b/src/plugins/kibana_react/public/index.ts
index 9ad9f14ac5659..9bec91b859ab7 100644
--- a/src/plugins/kibana_react/public/index.ts
+++ b/src/plugins/kibana_react/public/index.ts
@@ -25,7 +25,7 @@ export * from './ui_settings';
export * from './field_icon';
export * from './table_list_view';
export * from './split_panel';
-export { ValidatedDualRange } from './validated_range';
+export { ValidatedDualRange, Value } from './validated_range';
export * from './notifications';
export { Markdown, MarkdownSimple } from './markdown';
export { reactToUiComponent, uiToReactComponent } from './adapters';
diff --git a/src/plugins/kibana_react/public/validated_range/index.ts b/src/plugins/kibana_react/public/validated_range/index.ts
index bc643373f5e60..7d720ec842a43 100644
--- a/src/plugins/kibana_react/public/validated_range/index.ts
+++ b/src/plugins/kibana_react/public/validated_range/index.ts
@@ -17,4 +17,4 @@
* under the License.
*/
-export { ValidatedDualRange } from './validated_dual_range';
+export { ValidatedDualRange, Value } from './validated_dual_range';
diff --git a/src/plugins/kibana_utils/public/core/create_start_service_getter.test.ts b/src/plugins/kibana_utils/public/core/create_start_service_getter.test.ts
new file mode 100644
index 0000000000000..9d9b21269e102
--- /dev/null
+++ b/src/plugins/kibana_utils/public/core/create_start_service_getter.test.ts
@@ -0,0 +1,78 @@
+/*
+ * 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 { StartServicesAccessor } from '../../../../core/public';
+import { createStartServicesGetter } from './create_start_service_getter';
+import { Defer } from '../../common/defer';
+
+describe('createStartServicesGetter', () => {
+ test('throws if services are accessed before accessor resolves', async () => {
+ const future = new Defer();
+ const accessor: StartServicesAccessor = async () => await future.promise;
+ const start = createStartServicesGetter(accessor);
+
+ await new Promise(r => setTimeout(r, 1));
+
+ expect(() => start()).toThrowErrorMatchingInlineSnapshot(
+ `"Trying to access start services before start."`
+ );
+ });
+
+ test('returns services after accessor resolves even if first time called before it resolved', async () => {
+ const future = new Defer();
+ const core = {};
+ const plugins = {};
+ const self = {};
+ const accessor: StartServicesAccessor = async () => await future.promise;
+ const start = createStartServicesGetter(accessor);
+
+ await new Promise(r => setTimeout(r, 1));
+
+ expect(() => start()).toThrow();
+
+ await new Promise(r => setTimeout(r, 1));
+ future.resolve([core, plugins, self]);
+ await future.promise;
+
+ expect(start()).toEqual({
+ core,
+ plugins,
+ self,
+ });
+ });
+
+ test('returns services if called after accessor resolves', async () => {
+ const future = new Defer();
+ const core = {};
+ const plugins = {};
+ const self = {};
+ const accessor: StartServicesAccessor = async () => await future.promise;
+ const start = createStartServicesGetter(accessor);
+
+ await new Promise(r => setTimeout(r, 1));
+ future.resolve([core, plugins, self]);
+ await future.promise;
+
+ expect(start()).toEqual({
+ core,
+ plugins,
+ self,
+ });
+ });
+});
diff --git a/src/plugins/kibana_utils/public/core/create_start_service_getter.ts b/src/plugins/kibana_utils/public/core/create_start_service_getter.ts
new file mode 100644
index 0000000000000..5e385eb5ed473
--- /dev/null
+++ b/src/plugins/kibana_utils/public/core/create_start_service_getter.ts
@@ -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 { CoreStart, StartServicesAccessor } from '../../../../core/public';
+
+export interface StartServices {
+ plugins: Plugins;
+ self: OwnContract;
+ core: CoreStart;
+}
+
+export type StartServicesGetter = () => StartServices<
+ Plugins,
+ OwnContract
+>;
+
+/**
+ * Use this utility to create a synchronous *start* service getter in *setup*
+ * life-cycle of your plugin.
+ *
+ * Below is a usage example in a Kibana plugin.
+ *
+ * ```ts
+ * export interface MyPluginStartDeps {
+ * data: DataPublicPluginStart;
+ * expressions: ExpressionsStart;
+ * inspector: InspectorStart;
+ * uiActions: UiActionsStart;
+ * }
+ *
+ * class MyPlugin implements Plugin {
+ * setup(core: CoreSetup, plugins) {
+ * const start = createStartServicesGetter(core.getStartServices);
+ * plugins.expressions.registerFunction(myExpressionFunction(start));
+ * }
+ *
+ * start(core, plugins: MyPluginStartDeps) {
+ *
+ * }
+ * }
+ * ```
+ *
+ * In `myExpressionFunction` you can make sure you are picking only the dependencies
+ * your function needs using the `Pick` type.
+ *
+ * ```ts
+ * const myExpressionFunction =
+ * (start: StartServicesGetter>) => {
+ *
+ * start().plugins.indexPatterns.something(123);
+ * }
+ * ```
+ *
+ * @param accessor Asynchronous start service accessor provided by platform.
+ * @returns Returns a function which synchronously returns *start* core services
+ * and plugin contracts. If you call this function before the *start* life-cycle
+ * has started it will throw.
+ */
+export const createStartServicesGetter = (
+ accessor: StartServicesAccessor
+): StartServicesGetter => {
+ let services: StartServices | undefined;
+
+ accessor().then(
+ ([core, plugins, self]) => {
+ services = {
+ core,
+ plugins,
+ self,
+ };
+ },
+ error => {
+ // eslint-disable-next-line no-console
+ console.error('Could not access start services.', error);
+ }
+ );
+
+ return () => {
+ if (!services) throw new Error('Trying to access start services before start.');
+ return services;
+ };
+};
diff --git a/src/plugins/kibana_utils/public/core/index.ts b/src/plugins/kibana_utils/public/core/index.ts
index 3f08d591300a2..8bbb2129071f5 100644
--- a/src/plugins/kibana_utils/public/core/index.ts
+++ b/src/plugins/kibana_utils/public/core/index.ts
@@ -18,3 +18,4 @@
*/
export * from './create_kibana_utils_core';
+export * from './create_start_service_getter';
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/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/maps_legacy/public/__tests__/map/service_settings.js b/src/plugins/maps_legacy/public/__tests__/map/service_settings.js
index a9272ea396639..4cbe098501c67 100644
--- a/src/plugins/maps_legacy/public/__tests__/map/service_settings.js
+++ b/src/plugins/maps_legacy/public/__tests__/map/service_settings.js
@@ -143,24 +143,24 @@ describe('service_settings (FKA tilemaptest)', function() {
}
it('accepts an object', async () => {
- serviceSettings.addQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'bar' });
});
it('merged additions with previous values', async () => {
// ensure that changes are always additive
- serviceSettings.addQueryParams({ foo: 'bar' });
- serviceSettings.addQueryParams({ bar: 'stool' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ bar: 'stool' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'bar', bar: 'stool' });
});
it('overwrites conflicting previous values', async () => {
// ensure that conflicts are overwritten
- serviceSettings.addQueryParams({ foo: 'bar' });
- serviceSettings.addQueryParams({ bar: 'stool' });
- serviceSettings.addQueryParams({ foo: 'tstool' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ bar: 'stool' });
+ serviceSettings.setQueryParams({ foo: 'tstool' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'tstool', bar: 'stool' });
});
@@ -168,7 +168,7 @@ describe('service_settings (FKA tilemaptest)', function() {
it('when overridden, should continue to work', async () => {
mapConfig.emsFileApiUrl = emsFileApiUrl2;
mapConfig.emsTileApiUrl = emsTileApiUrl2;
- serviceSettings.addQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
tilemapServices = await serviceSettings.getTMSServices();
await assertQuery({ foo: 'bar' });
});
@@ -292,7 +292,7 @@ describe('service_settings (FKA tilemaptest)', function() {
describe('File layers', function() {
it('should load manifest (all props)', async function() {
- serviceSettings.addQueryParams({ foo: 'bar' });
+ serviceSettings.setQueryParams({ foo: 'bar' });
const fileLayers = await serviceSettings.getFileLayers();
expect(fileLayers.length).to.be(18);
const assertions = fileLayers.map(async function(fileLayer) {
diff --git a/src/plugins/maps_legacy/public/map/service_settings.js b/src/plugins/maps_legacy/public/map/service_settings.js
index 11c853d39e107..f4f0d66ee20de 100644
--- a/src/plugins/maps_legacy/public/map/service_settings.js
+++ b/src/plugins/maps_legacy/public/map/service_settings.js
@@ -69,6 +69,10 @@ export class ServiceSettings {
return origin === ORIGIN.EMS && this._showZoomMessage;
}
+ enableZoomMessage() {
+ this._showZoomMessage = true;
+ }
+
disableZoomMessage() {
this._showZoomMessage = false;
}
@@ -148,11 +152,12 @@ export class ServiceSettings {
}
/**
- * Add optional query-parameters to all requests
+ * Set optional query-parameters for all requests
*
* @param additionalQueryParams
*/
- addQueryParams(additionalQueryParams) {
+ setQueryParams(additionalQueryParams) {
+ // Functions more as a "set" than an "add" in ems-client
this._emsClient.addQueryParams(additionalQueryParams);
}
diff --git a/src/plugins/saved_objects_management/server/services/management.test.ts b/src/plugins/saved_objects_management/server/services/management.test.ts
index 6b95048749fae..3625a3f913444 100644
--- a/src/plugins/saved_objects_management/server/services/management.test.ts
+++ b/src/plugins/saved_objects_management/server/services/management.test.ts
@@ -28,7 +28,7 @@ describe('SavedObjectsManagement', () => {
registry.registerType({
name: 'unknown',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: { properties: {} },
migrations: {},
...type,
diff --git a/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts b/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts
index 9f997ab7b5df3..a0de79da565e6 100644
--- a/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts
+++ b/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts
@@ -33,7 +33,7 @@ export function registerMappings(registerType: SavedObjectsServiceSetup['registe
registerType({
name: 'application_usage_totals',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
appId: { type: 'keyword' },
@@ -46,7 +46,7 @@ export function registerMappings(registerType: SavedObjectsServiceSetup['registe
registerType({
name: 'application_usage_transactional',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
timestamp: { type: 'date' },
diff --git a/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts b/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
index 3f6e1836cac7d..603742f612a6b 100644
--- a/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
+++ b/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
@@ -38,7 +38,7 @@ export function registerUiMetricUsageCollector(
registerType({
name: 'ui-metric',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
count: {
diff --git a/src/plugins/telemetry/server/config.ts b/src/plugins/telemetry/server/config.ts
index 9621a8b5619b2..99dde0c3b3d96 100644
--- a/src/plugins/telemetry/server/config.ts
+++ b/src/plugins/telemetry/server/config.ts
@@ -36,8 +36,8 @@ export const configSchema = schema.object({
config: schema.string({ defaultValue: getConfigPath() }),
banner: schema.boolean({ defaultValue: true }),
url: schema.conditional(
- schema.contextRef('dev'),
- schema.literal(true),
+ schema.contextRef('dist'),
+ schema.literal(false), // Point to staging if it's not a distributable release
schema.string({
defaultValue: `https://telemetry-staging.elastic.co/xpack/${ENDPOINT_VERSION}/send`,
}),
@@ -46,8 +46,8 @@ export const configSchema = schema.object({
})
),
optInStatusUrl: schema.conditional(
- schema.contextRef('dev'),
- schema.literal(true),
+ schema.contextRef('dist'),
+ schema.literal(false), // Point to staging if it's not a distributable release
schema.string({
defaultValue: `https://telemetry-staging.elastic.co/opt_in_status/${ENDPOINT_VERSION}/send`,
}),
diff --git a/src/plugins/telemetry/server/plugin.ts b/src/plugins/telemetry/server/plugin.ts
index 1df6a665e4d76..d1530c272027a 100644
--- a/src/plugins/telemetry/server/plugin.ts
+++ b/src/plugins/telemetry/server/plugin.ts
@@ -125,7 +125,7 @@ export class TelemetryPlugin implements Plugin {
registerType({
name: 'telemetry',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
enabled: {
diff --git a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts
index c04625eb1dd42..6d64268569e06 100644
--- a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts
+++ b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.test.ts
@@ -22,14 +22,14 @@ import { encryptTelemetry, getKID } from './encrypt';
describe('getKID', () => {
it(`returns 'kibana_dev' kid for development`, async () => {
- const isProd = false;
- const kid = getKID(isProd);
+ const useProdKey = false;
+ const kid = getKID(useProdKey);
expect(kid).toBe('kibana_dev');
});
it(`returns 'kibana_prod' kid for development`, async () => {
- const isProd = true;
- const kid = getKID(isProd);
+ const useProdKey = true;
+ const kid = getKID(useProdKey);
expect(kid).toBe('kibana');
});
});
@@ -41,19 +41,19 @@ describe('encryptTelemetry', () => {
it('encrypts payload', async () => {
const payload = { some: 'value' };
- await encryptTelemetry(payload, { isProd: true });
+ await encryptTelemetry(payload, { useProdKey: true });
expect(createRequestEncryptor).toBeCalledWith(telemetryJWKS);
});
- it('uses kibana kid on { isProd: true }', async () => {
+ it('uses kibana kid on { useProdKey: true }', async () => {
const payload = { some: 'value' };
- await encryptTelemetry(payload, { isProd: true });
+ await encryptTelemetry(payload, { useProdKey: true });
expect(mockEncrypt).toBeCalledWith('kibana', payload);
});
- it('uses kibana_dev kid on { isProd: false }', async () => {
+ it('uses kibana_dev kid on { useProdKey: false }', async () => {
const payload = { some: 'value' };
- await encryptTelemetry(payload, { isProd: false });
+ await encryptTelemetry(payload, { useProdKey: false });
expect(mockEncrypt).toBeCalledWith('kibana_dev', payload);
});
});
diff --git a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts
index 44f053064cfcb..89f34d794f059 100644
--- a/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts
+++ b/src/plugins/telemetry_collection_manager/server/encryption/encrypt.ts
@@ -20,12 +20,15 @@
import { createRequestEncryptor } from '@elastic/request-crypto';
import { telemetryJWKS } from './telemetry_jwks';
-export function getKID(isProd = false): string {
- return isProd ? 'kibana' : 'kibana_dev';
+export function getKID(useProdKey = false): string {
+ return useProdKey ? 'kibana' : 'kibana_dev';
}
-export async function encryptTelemetry(payload: any, { isProd = false } = {}): Promise {
- const kid = getKID(isProd);
+export async function encryptTelemetry(
+ payload: any,
+ { useProdKey = false } = {}
+): Promise {
+ const kid = getKID(useProdKey);
const encryptor = await createRequestEncryptor(telemetryJWKS);
const clusters = [].concat(payload);
return Promise.all(clusters.map((cluster: any) => encryptor.encrypt(kid, cluster)));
diff --git a/src/plugins/telemetry_collection_manager/server/plugin.ts b/src/plugins/telemetry_collection_manager/server/plugin.ts
index f2f20e215c535..0b57fae83c0fb 100644
--- a/src/plugins/telemetry_collection_manager/server/plugin.ts
+++ b/src/plugins/telemetry_collection_manager/server/plugin.ts
@@ -50,12 +50,12 @@ export class TelemetryCollectionManagerPlugin
private readonly collections: Array> = [];
private usageGetterMethodPriority = -1;
private usageCollection?: UsageCollectionSetup;
- private readonly isDev: boolean;
+ private readonly isDistributable: boolean;
private readonly version: string;
constructor(initializerContext: PluginInitializerContext) {
this.logger = initializerContext.logger.get();
- this.isDev = initializerContext.env.mode.dev;
+ this.isDistributable = initializerContext.env.packageInfo.dist;
this.version = initializerContext.env.packageInfo.version;
}
@@ -158,7 +158,7 @@ export class TelemetryCollectionManagerPlugin
if (config.unencrypted) {
return optInStats;
}
- return encryptTelemetry(optInStats, { isProd: !this.isDev });
+ return encryptTelemetry(optInStats, { useProdKey: this.isDistributable });
}
} catch (err) {
this.logger.debug(`Failed to collect any opt in stats with registered collections.`);
@@ -176,7 +176,6 @@ export class TelemetryCollectionManagerPlugin
) => {
const context: StatsCollectionContext = {
logger: this.logger.get(collection.title),
- isDev: this.isDev,
version: this.version,
...collection.customContext,
};
@@ -206,7 +205,7 @@ export class TelemetryCollectionManagerPlugin
return usageData;
}
- return encryptTelemetry(usageData, { isProd: !this.isDev });
+ return encryptTelemetry(usageData, { useProdKey: this.isDistributable });
}
} catch (err) {
this.logger.debug(
@@ -225,7 +224,6 @@ export class TelemetryCollectionManagerPlugin
): Promise {
const context: StatsCollectionContext = {
logger: this.logger.get(collection.title),
- isDev: this.isDev,
version: this.version,
...collection.customContext,
};
diff --git a/src/plugins/telemetry_collection_manager/server/types.ts b/src/plugins/telemetry_collection_manager/server/types.ts
index e23d6a4c388f4..d3a47694d38a7 100644
--- a/src/plugins/telemetry_collection_manager/server/types.ts
+++ b/src/plugins/telemetry_collection_manager/server/types.ts
@@ -101,7 +101,6 @@ export interface ESLicense {
export interface StatsCollectionContext {
logger: Logger;
- isDev: boolean;
version: string;
}
diff --git a/src/plugins/timelion/kibana.json b/src/plugins/timelion/kibana.json
deleted file mode 100644
index dddfd6c67e655..0000000000000
--- a/src/plugins/timelion/kibana.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "id": "timelion",
- "version": "8.0.0",
- "kibanaVersion": "kibana",
- "configPath": ["timelion"],
- "server": true,
- "ui": true
-}
diff --git a/src/plugins/vis_default_editor/public/components/agg_params_map.ts b/src/plugins/vis_default_editor/public/components/agg_params_map.ts
index 5af3cfc5b0928..9bc3146b9903b 100644
--- a/src/plugins/vis_default_editor/public/components/agg_params_map.ts
+++ b/src/plugins/vis_default_editor/public/components/agg_params_map.ts
@@ -58,6 +58,8 @@ const buckets = {
size: controls.SizeParamEditor,
},
[BUCKET_TYPES.TERMS]: {
+ include: controls.IncludeExcludeParamEditor,
+ exclude: controls.IncludeExcludeParamEditor,
orderBy: controls.OrderByParamEditor,
orderAgg: controls.OrderAggParamEditor,
order: wrapWithInlineComp(controls.OrderParamEditor),
diff --git a/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts
index dac86249ebbb9..5cb594ade8dba 100644
--- a/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts
+++ b/src/plugins/vis_default_editor/public/components/controls/components/number_list/utils.ts
@@ -119,7 +119,7 @@ function getNextModel(list: NumberRowModel[], range: NumberListRange): NumberRow
};
}
-function getInitModelList(list: Array): NumberRowModel[] {
+function getInitModelList(list: Array): NumberRowModel[] {
return list.length
? list.map(num => ({
value: (num === undefined ? EMPTY_STRING : num) as NumberRowModel['value'],
diff --git a/src/plugins/vis_default_editor/public/components/controls/components/simple_number_list.tsx b/src/plugins/vis_default_editor/public/components/controls/components/simple_number_list.tsx
new file mode 100644
index 0000000000000..becf8e47ef573
--- /dev/null
+++ b/src/plugins/vis_default_editor/public/components/controls/components/simple_number_list.tsx
@@ -0,0 +1,140 @@
+/*
+ * 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 React, { Fragment, useCallback, useEffect, useMemo, useState } from 'react';
+import { isArray } from 'lodash';
+import { EuiButtonEmpty, EuiFlexItem, EuiFormRow, EuiSpacer, htmlIdGenerator } from '@elastic/eui';
+import { FormattedMessage } from '@kbn/i18n/react';
+import { EMPTY_STRING, getInitModelList, getRange, parse } from './number_list/utils';
+import { NumberRow, NumberRowModel } from './number_list/number_row';
+import { AggParamEditorProps } from '../../agg_param_props';
+
+const generateId = htmlIdGenerator();
+
+function SimpleNumberList({
+ agg,
+ aggParam,
+ value,
+ setValue,
+ setTouched,
+}: AggParamEditorProps>) {
+ const [numbers, setNumbers] = useState(
+ getInitModelList(value && isArray(value) ? value : [EMPTY_STRING])
+ );
+ const numberRange = useMemo(() => getRange('[-Infinity,Infinity]'), []);
+
+ // This useEffect is needed to discard changes, it sets numbers a mapped value if they are different
+ useEffect(() => {
+ if (
+ isArray(value) &&
+ (value.length !== numbers.length ||
+ !value.every((numberValue, index) => numberValue === numbers[index].value))
+ ) {
+ setNumbers(
+ value.map(numberValue => ({
+ id: generateId(),
+ value: numberValue,
+ isInvalid: false,
+ }))
+ );
+ }
+ }, [numbers, value]);
+
+ const onUpdate = useCallback(
+ (numberList: NumberRowModel[]) => {
+ setNumbers(numberList);
+ setValue(numberList.map(({ value: numberValue }) => numberValue));
+ },
+ [setValue]
+ );
+
+ const onChangeValue = useCallback(
+ (numberField: { id: string; value: string }) => {
+ onUpdate(
+ numbers.map(number =>
+ number.id === numberField.id
+ ? {
+ id: numberField.id,
+ value: parse(numberField.value),
+ isInvalid: false,
+ }
+ : number
+ )
+ );
+ },
+ [numbers, onUpdate]
+ );
+
+ // Add an item to the end of the list
+ const onAdd = useCallback(() => {
+ const newArray = [
+ ...numbers,
+ {
+ id: generateId(),
+ value: EMPTY_STRING as '',
+ isInvalid: false,
+ },
+ ];
+ onUpdate(newArray);
+ }, [numbers, onUpdate]);
+
+ const onDelete = useCallback(
+ (id: string) => onUpdate(numbers.filter(number => number.id !== id)),
+ [numbers, onUpdate]
+ );
+
+ return (
+
+ <>
+ {numbers.map((number, arrayIndex) => (
+
+
+ {numbers.length - 1 !== arrayIndex && }
+
+ ))}
+
+
+
+
+
+
+ >
+
+ );
+}
+
+export { SimpleNumberList };
diff --git a/src/plugins/vis_default_editor/public/components/controls/include_exclude.tsx b/src/plugins/vis_default_editor/public/components/controls/include_exclude.tsx
new file mode 100644
index 0000000000000..f60f6ce7ce249
--- /dev/null
+++ b/src/plugins/vis_default_editor/public/components/controls/include_exclude.tsx
@@ -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 React, { useEffect } from 'react';
+import { AggParamEditorProps } from '../agg_param_props';
+import { StringParamEditor } from './string';
+import { search } from '../../../../data/public';
+import { SimpleNumberList } from './components/simple_number_list';
+const { isNumberType } = search.aggs;
+
+export function IncludeExcludeParamEditor(props: AggParamEditorProps>) {
+ const { agg, value, setValue } = props;
+ const isAggOfNumberType = isNumberType(agg);
+
+ // This useEffect converts value from string type to number and back when the field type is changed
+ useEffect(() => {
+ if (isAggOfNumberType && !Array.isArray(value) && value !== undefined) {
+ const numberArray = value
+ .split('|')
+ .map(item => parseFloat(item))
+ .filter(number => Number.isFinite(number));
+ setValue(numberArray.length ? numberArray : ['']);
+ } else if (!isAggOfNumberType && Array.isArray(value) && value !== undefined) {
+ setValue(value.filter(item => item !== '').join('|'));
+ }
+ }, [isAggOfNumberType, setValue, value]);
+
+ return isAggOfNumberType ? (
+ } />
+ ) : (
+
+ );
+}
diff --git a/src/plugins/vis_default_editor/public/components/controls/index.ts b/src/plugins/vis_default_editor/public/components/controls/index.ts
index e8944aa667853..cfb236e5e22e3 100644
--- a/src/plugins/vis_default_editor/public/components/controls/index.ts
+++ b/src/plugins/vis_default_editor/public/components/controls/index.ts
@@ -24,6 +24,7 @@ export { ExtendedBoundsParamEditor } from './extended_bounds';
export { FieldParamEditor } from './field';
export { FiltersParamEditor } from './filters';
export { HasExtendedBoundsParamEditor } from './has_extended_bounds';
+export { IncludeExcludeParamEditor } from './include_exclude';
export { IpRangesParamEditor } from './ip_ranges';
export { IpRangeTypeParamEditor } from './ip_range_type';
export { IsFilteredByCollarParamEditor } from './is_filtered_by_collar';
diff --git a/src/plugins/vis_type_table/config.ts b/src/plugins/vis_type_table/config.ts
new file mode 100644
index 0000000000000..6749bd83de39f
--- /dev/null
+++ b/src/plugins/vis_type_table/config.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.
+ */
+
+import { schema, TypeOf } from '@kbn/config-schema';
+
+export const configSchema = schema.object({
+ enabled: schema.boolean({ defaultValue: true }),
+});
+
+export type ConfigSchema = TypeOf;
diff --git a/src/plugins/vis_type_table/kibana.json b/src/plugins/vis_type_table/kibana.json
new file mode 100644
index 0000000000000..bb0f6478a4240
--- /dev/null
+++ b/src/plugins/vis_type_table/kibana.json
@@ -0,0 +1,11 @@
+{
+ "id": "visTypeTable",
+ "version": "kibana",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": [
+ "expressions",
+ "visualizations",
+ "data"
+ ]
+}
diff --git a/src/legacy/core_plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap b/src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap
rename to src/plugins/vis_type_table/public/__snapshots__/table_vis_fn.test.ts.snap
diff --git a/src/legacy/core_plugins/vis_type_table/public/_table_vis.scss b/src/plugins/vis_type_table/public/_table_vis.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/_table_vis.scss
rename to src/plugins/vis_type_table/public/_table_vis.scss
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/_agg_table.scss b/src/plugins/vis_type_table/public/agg_table/_agg_table.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/agg_table/_agg_table.scss
rename to src/plugins/vis_type_table/public/agg_table/_agg_table.scss
diff --git a/src/plugins/vis_type_table/public/agg_table/_index.scss b/src/plugins/vis_type_table/public/agg_table/_index.scss
new file mode 100644
index 0000000000000..340e08a76f1bd
--- /dev/null
+++ b/src/plugins/vis_type_table/public/agg_table/_index.scss
@@ -0,0 +1 @@
+@import './agg_table';
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.html b/src/plugins/vis_type_table/public/agg_table/agg_table.html
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.html
rename to src/plugins/vis_type_table/public/agg_table/agg_table.html
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.js b/src/plugins/vis_type_table/public/agg_table/agg_table.js
similarity index 98%
rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.js
rename to src/plugins/vis_type_table/public/agg_table/agg_table.js
index b9e79f96e4fc1..0cd501e2d0344 100644
--- a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table.js
+++ b/src/plugins/vis_type_table/public/agg_table/agg_table.js
@@ -238,9 +238,9 @@ export function KbnAggTable(config, RecursionHelper) {
}
/**
- * @param {[]Object} columns - the formatted columns that will be displayed
+ * @param {Object[]} columns - the formatted columns that will be displayed
* @param {String} title - the title of the column to add to
- * @param {[]Object} rows - the row data for the columns
+ * @param {Object[]} rows - the row data for the columns
* @param {Number} insertAtIndex - the index to insert the percentage column at
* @returns {Object} - cols and rows for the table to render now included percentage column(s)
*/
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.html b/src/plugins/vis_type_table/public/agg_table/agg_table_group.html
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.html
rename to src/plugins/vis_type_table/public/agg_table/agg_table_group.html
diff --git a/src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.js b/src/plugins/vis_type_table/public/agg_table/agg_table_group.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/agg_table/agg_table_group.js
rename to src/plugins/vis_type_table/public/agg_table/agg_table_group.js
diff --git a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx b/src/plugins/vis_type_table/public/components/table_vis_options.tsx
similarity index 96%
rename from src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx
rename to src/plugins/vis_type_table/public/components/table_vis_options.tsx
index 265528f33f9cd..68348d5ef1060 100644
--- a/src/legacy/core_plugins/vis_type_table/public/components/table_vis_options.tsx
+++ b/src/plugins/vis_type_table/public/components/table_vis_options.tsx
@@ -24,12 +24,8 @@ import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import { VisOptionsProps } from 'src/plugins/vis_default_editor/public';
-import { search } from '../../../../../plugins/data/public';
-import {
- SwitchOption,
- SelectOption,
- NumberInputOption,
-} from '../../../../../plugins/charts/public';
+import { search } from '../../../data/public';
+import { SwitchOption, SelectOption, NumberInputOption } from '../../../charts/public';
import { TableVisParams } from '../types';
import { totalAggregations } from './utils';
diff --git a/src/legacy/core_plugins/vis_type_table/public/components/utils.ts b/src/plugins/vis_type_table/public/components/utils.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/components/utils.ts
rename to src/plugins/vis_type_table/public/components/utils.ts
diff --git a/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts b/src/plugins/vis_type_table/public/get_inner_angular.ts
similarity index 91%
rename from src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts
rename to src/plugins/vis_type_table/public/get_inner_angular.ts
index 6208e358b4184..d69b9bba31b03 100644
--- a/src/legacy/core_plugins/vis_type_table/public/get_inner_angular.ts
+++ b/src/plugins/vis_type_table/public/get_inner_angular.ts
@@ -23,7 +23,7 @@
import angular from 'angular';
import 'angular-recursion';
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
-import { CoreStart, LegacyCoreStart, IUiSettingsClient } from 'kibana/public';
+import { CoreStart, IUiSettingsClient, PluginInitializerContext } from 'kibana/public';
import {
initAngularBootstrap,
PaginateDirectiveProvider,
@@ -32,15 +32,15 @@ import {
watchMultiDecorator,
KbnAccessibleClickProvider,
configureAppAngularModule,
-} from '../../../../plugins/kibana_legacy/public';
+} from '../../kibana_legacy/public';
initAngularBootstrap();
const thirdPartyAngularDependencies = ['ngSanitize', 'ui.bootstrap', 'RecursionHelper'];
-export function getAngularModule(name: string, core: CoreStart) {
+export function getAngularModule(name: string, core: CoreStart, context: PluginInitializerContext) {
const uiModule = getInnerAngular(name, core);
- configureAppAngularModule(uiModule, core as LegacyCoreStart, true);
+ configureAppAngularModule(uiModule, { core, env: context.env }, true);
return uiModule;
}
diff --git a/src/legacy/core_plugins/vis_type_table/public/index.scss b/src/plugins/vis_type_table/public/index.scss
similarity index 61%
rename from src/legacy/core_plugins/vis_type_table/public/index.scss
rename to src/plugins/vis_type_table/public/index.scss
index 54124ebc42620..0972c85e0dbe0 100644
--- a/src/legacy/core_plugins/vis_type_table/public/index.scss
+++ b/src/plugins/vis_type_table/public/index.scss
@@ -1,5 +1,3 @@
-@import 'src/legacy/ui/public/styles/styling_constants';
-
// Prefix all styles with "tbv" to avoid conflicts.
// Examples
// tbvChart
@@ -7,6 +5,6 @@
// tbvChart__legend--small
// tbvChart__legend-isLoading
-@import 'agg_table/index';
-@import 'paginated_table/index';
+@import './agg_table/index';
+@import './paginated_table/index';
@import './table_vis';
diff --git a/src/legacy/core_plugins/vis_type_table/public/index.ts b/src/plugins/vis_type_table/public/index.ts
similarity index 92%
rename from src/legacy/core_plugins/vis_type_table/public/index.ts
rename to src/plugins/vis_type_table/public/index.ts
index efbaf69659ea2..5621fdb094772 100644
--- a/src/legacy/core_plugins/vis_type_table/public/index.ts
+++ b/src/plugins/vis_type_table/public/index.ts
@@ -16,8 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-
-import { PluginInitializerContext } from '../../../../core/public';
+import './index.scss';
+import { PluginInitializerContext } from 'kibana/public';
import { TableVisPlugin as Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
diff --git a/src/plugins/vis_type_table/public/paginated_table/_index.scss b/src/plugins/vis_type_table/public/paginated_table/_index.scss
new file mode 100644
index 0000000000000..23d56c09b2818
--- /dev/null
+++ b/src/plugins/vis_type_table/public/paginated_table/_index.scss
@@ -0,0 +1 @@
+@import './_table_cell_filter';
diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss b/src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss
rename to src/plugins/vis_type_table/public/paginated_table/_table_cell_filter.scss
diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.html b/src/plugins/vis_type_table/public/paginated_table/paginated_table.html
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.html
rename to src/plugins/vis_type_table/public/paginated_table/paginated_table.html
diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.js b/src/plugins/vis_type_table/public/paginated_table/paginated_table.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.js
rename to src/plugins/vis_type_table/public/paginated_table/paginated_table.js
diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts b/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts
similarity index 98%
rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts
rename to src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts
index 7352236f03feb..23e4aee0378dc 100644
--- a/src/legacy/core_plugins/vis_type_table/public/paginated_table/paginated_table.test.ts
+++ b/src/plugins/vis_type_table/public/paginated_table/paginated_table.test.ts
@@ -25,10 +25,9 @@ import 'angular-mocks';
import { getAngularModule } from '../get_inner_angular';
import { initTableVisLegacyModule } from '../table_vis_legacy_module';
-import { coreMock } from '../../../../../core/public/mocks';
+import { coreMock } from '../../../../core/public/mocks';
-jest.mock('ui/new_platform');
-jest.mock('../../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({
+jest.mock('../../../kibana_legacy/public/angular/angular_config', () => ({
configureAppAngularModule: () => {},
}));
@@ -73,7 +72,11 @@ describe('Table Vis - Paginated table', () => {
let paginatedTable: any;
const initLocalAngular = () => {
- const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart());
+ const tableVisModule = getAngularModule(
+ 'kibana/table_vis',
+ coreMock.createStart(),
+ coreMock.createPluginInitializerContext()
+ );
initTableVisLegacyModule(tableVisModule);
};
diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/rows.js b/src/plugins/vis_type_table/public/paginated_table/rows.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/rows.js
rename to src/plugins/vis_type_table/public/paginated_table/rows.js
diff --git a/src/legacy/core_plugins/vis_type_table/public/paginated_table/table_cell_filter.html b/src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/paginated_table/table_cell_filter.html
rename to src/plugins/vis_type_table/public/paginated_table/table_cell_filter.html
diff --git a/src/legacy/core_plugins/vis_type_table/public/plugin.ts b/src/plugins/vis_type_table/public/plugin.ts
similarity index 80%
rename from src/legacy/core_plugins/vis_type_table/public/plugin.ts
rename to src/plugins/vis_type_table/public/plugin.ts
index ea12a5320a14d..a41d939523bcc 100644
--- a/src/legacy/core_plugins/vis_type_table/public/plugin.ts
+++ b/src/plugins/vis_type_table/public/plugin.ts
@@ -16,14 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Plugin as ExpressionsPublicPlugin } from '../../../../plugins/expressions/public';
-import { VisualizationsSetup } from '../../../../plugins/visualizations/public';
-
-import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from '../../../../core/public';
+import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'kibana/public';
+import { Plugin as ExpressionsPublicPlugin } from '../../expressions/public';
+import { VisualizationsSetup } from '../../visualizations/public';
import { createTableVisFn } from './table_vis_fn';
-import { tableVisTypeDefinition } from './table_vis_type';
-import { DataPublicPluginStart } from '../../../../plugins/data/public';
+import { getTableVisTypeDefinition } from './table_vis_type';
+import { DataPublicPluginStart } from '../../data/public';
import { setFormatService } from './services';
/** @internal */
@@ -40,6 +39,7 @@ export interface TablePluginStartDependencies {
/** @internal */
export class TableVisPlugin implements Plugin, void> {
initializerContext: PluginInitializerContext;
+ createBaseVisualization: any;
constructor(initializerContext: PluginInitializerContext) {
this.initializerContext = initializerContext;
@@ -50,8 +50,9 @@ export class TableVisPlugin implements Plugin, void> {
{ expressions, visualizations }: TablePluginSetupDependencies
) {
expressions.registerFunction(createTableVisFn);
-
- visualizations.createBaseVisualization(tableVisTypeDefinition);
+ visualizations.createBaseVisualization(
+ getTableVisTypeDefinition(core, this.initializerContext)
+ );
}
public start(core: CoreStart, { data }: TablePluginStartDependencies) {
diff --git a/src/legacy/core_plugins/vis_type_table/public/services.ts b/src/plugins/vis_type_table/public/services.ts
similarity index 86%
rename from src/legacy/core_plugins/vis_type_table/public/services.ts
rename to src/plugins/vis_type_table/public/services.ts
index b4b491ac7a555..3aaffe75e27f1 100644
--- a/src/legacy/core_plugins/vis_type_table/public/services.ts
+++ b/src/plugins/vis_type_table/public/services.ts
@@ -17,8 +17,8 @@
* under the License.
*/
-import { createGetterSetter } from '../../../../plugins/kibana_utils/public';
-import { DataPublicPluginStart } from '../../../../plugins/data/public';
+import { createGetterSetter } from '../../kibana_utils/public';
+import { DataPublicPluginStart } from '../../data/public';
export const [getFormatService, setFormatService] = createGetterSetter<
DataPublicPluginStart['fieldFormats']
diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis.html b/src/plugins/vis_type_table/public/table_vis.html
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/table_vis.html
rename to src/plugins/vis_type_table/public/table_vis.html
diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.js b/src/plugins/vis_type_table/public/table_vis_controller.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/table_vis_controller.js
rename to src/plugins/vis_type_table/public/table_vis_controller.js
diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts b/src/plugins/vis_type_table/public/table_vis_controller.test.ts
similarity index 89%
rename from src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts
rename to src/plugins/vis_type_table/public/table_vis_controller.test.ts
index 8d6f88bf8dd4a..4607324ca150c 100644
--- a/src/legacy/core_plugins/vis_type_table/public/table_vis_controller.test.ts
+++ b/src/plugins/vis_type_table/public/table_vis_controller.test.ts
@@ -26,24 +26,23 @@ import $ from 'jquery';
import StubIndexPattern from 'test_utils/stub_index_pattern';
import { getAngularModule } from './get_inner_angular';
import { initTableVisLegacyModule } from './table_vis_legacy_module';
-import { tableVisTypeDefinition } from './table_vis_type';
-import { Vis } from '../../../../plugins/visualizations/public';
+import { getTableVisTypeDefinition } from './table_vis_type';
+import { Vis } from '../../visualizations/public';
// eslint-disable-next-line
-import { stubFields } from '../../../../plugins/data/public/stubs';
+import { stubFields } from '../../data/public/stubs';
// eslint-disable-next-line
import { tableVisResponseHandler } from './table_vis_response_handler';
-import { coreMock } from '../../../../core/public/mocks';
+import { coreMock } from '../../../core/public/mocks';
+import { IAggConfig, search } from '../../data/public';
+// TODO: remove linting disable
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { npStart } from './legacy_imports';
-import { IAggConfig, search } from '../../../../plugins/data/public';
+import { searchStartMock } from '../../data/public/search/mocks';
-// should be mocked once get rid of 'ui/new_platform' legacy imports
-const { createAggConfigs } = npStart.plugins.data.search.aggs;
+const { createAggConfigs } = searchStartMock.aggs;
const { tabifyAggResponse } = search;
-jest.mock('ui/new_platform');
-jest.mock('../../../../plugins/kibana_legacy/public/angular/angular_config', () => ({
+jest.mock('../../kibana_legacy/public/angular/angular_config', () => ({
configureAppAngularModule: () => {},
}));
@@ -89,7 +88,11 @@ describe('Table Vis - Controller', () => {
let stubIndexPattern: any;
const initLocalAngular = () => {
- const tableVisModule = getAngularModule('kibana/table_vis', coreMock.createStart());
+ const tableVisModule = getAngularModule(
+ 'kibana/table_vis',
+ coreMock.createStart(),
+ coreMock.createPluginInitializerContext()
+ );
initTableVisLegacyModule(tableVisModule);
};
@@ -110,9 +113,13 @@ describe('Table Vis - Controller', () => {
(cfg: any) => cfg,
'time',
stubFields,
- coreMock.createStart()
+ coreMock.createSetup()
);
});
+ const tableVisTypeDefinition = getTableVisTypeDefinition(
+ coreMock.createSetup(),
+ coreMock.createPluginInitializerContext()
+ );
function getRangeVis(params?: object) {
return ({
diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts b/src/plugins/vis_type_table/public/table_vis_fn.test.ts
similarity index 95%
rename from src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts
rename to src/plugins/vis_type_table/public/table_vis_fn.test.ts
index 36392c10f93f3..9accf8950d910 100644
--- a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.test.ts
+++ b/src/plugins/vis_type_table/public/table_vis_fn.test.ts
@@ -21,7 +21,7 @@ import { createTableVisFn } from './table_vis_fn';
import { tableVisResponseHandler } from './table_vis_response_handler';
// eslint-disable-next-line
-import { functionWrapper } from '../../../../plugins/expressions/common/expression_functions/specs/tests/utils';
+import { functionWrapper } from '../../expressions/common/expression_functions/specs/tests/utils';
jest.mock('./table_vis_response_handler', () => ({
tableVisResponseHandler: jest.fn().mockReturnValue({
diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.ts b/src/plugins/vis_type_table/public/table_vis_fn.ts
similarity index 94%
rename from src/legacy/core_plugins/vis_type_table/public/table_vis_fn.ts
rename to src/plugins/vis_type_table/public/table_vis_fn.ts
index a97e596e89754..9739a7a284e6c 100644
--- a/src/legacy/core_plugins/vis_type_table/public/table_vis_fn.ts
+++ b/src/plugins/vis_type_table/public/table_vis_fn.ts
@@ -19,11 +19,7 @@
import { i18n } from '@kbn/i18n';
import { tableVisResponseHandler, TableContext } from './table_vis_response_handler';
-import {
- ExpressionFunctionDefinition,
- KibanaDatatable,
- Render,
-} from '../../../../plugins/expressions/public';
+import { ExpressionFunctionDefinition, KibanaDatatable, Render } from '../../expressions/public';
export type Input = KibanaDatatable;
diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_legacy_module.ts b/src/plugins/vis_type_table/public/table_vis_legacy_module.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/table_vis_legacy_module.ts
rename to src/plugins/vis_type_table/public/table_vis_legacy_module.ts
diff --git a/src/legacy/core_plugins/vis_type_table/public/table_vis_response_handler.ts b/src/plugins/vis_type_table/public/table_vis_response_handler.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_table/public/table_vis_response_handler.ts
rename to src/plugins/vis_type_table/public/table_vis_response_handler.ts
diff --git a/src/plugins/vis_type_table/public/table_vis_type.ts b/src/plugins/vis_type_table/public/table_vis_type.ts
new file mode 100644
index 0000000000000..26e5ac8cfd71a
--- /dev/null
+++ b/src/plugins/vis_type_table/public/table_vis_type.ts
@@ -0,0 +1,100 @@
+/*
+ * 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 { CoreSetup, PluginInitializerContext } from 'kibana/public';
+import { i18n } from '@kbn/i18n';
+import { AggGroupNames } from '../../data/public';
+import { Schemas } from '../../vis_default_editor/public';
+import { Vis } from '../../visualizations/public';
+import { tableVisResponseHandler } from './table_vis_response_handler';
+// @ts-ignore
+import tableVisTemplate from './table_vis.html';
+import { TableOptions } from './components/table_vis_options';
+import { getTableVisualizationControllerClass } from './vis_controller';
+
+export function getTableVisTypeDefinition(core: CoreSetup, context: PluginInitializerContext) {
+ return {
+ type: 'table',
+ name: 'table',
+ title: i18n.translate('visTypeTable.tableVisTitle', {
+ defaultMessage: 'Data Table',
+ }),
+ icon: 'visTable',
+ description: i18n.translate('visTypeTable.tableVisDescription', {
+ defaultMessage: 'Display values in a table',
+ }),
+ visualization: getTableVisualizationControllerClass(core, context),
+ visConfig: {
+ defaults: {
+ perPage: 10,
+ showPartialRows: false,
+ showMetricsAtAllLevels: false,
+ sort: {
+ columnIndex: null,
+ direction: null,
+ },
+ showTotal: false,
+ totalFunc: 'sum',
+ percentageCol: '',
+ },
+ template: tableVisTemplate,
+ },
+ editorConfig: {
+ optionsTemplate: TableOptions,
+ schemas: new Schemas([
+ {
+ group: AggGroupNames.Metrics,
+ name: 'metric',
+ title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.metricTitle', {
+ defaultMessage: 'Metric',
+ }),
+ aggFilter: ['!geo_centroid', '!geo_bounds'],
+ aggSettings: {
+ top_hits: {
+ allowStrings: true,
+ },
+ },
+ min: 1,
+ defaults: [{ type: 'count', schema: 'metric' }],
+ },
+ {
+ group: AggGroupNames.Buckets,
+ name: 'bucket',
+ title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.bucketTitle', {
+ defaultMessage: 'Split rows',
+ }),
+ aggFilter: ['!filter'],
+ },
+ {
+ group: AggGroupNames.Buckets,
+ name: 'split',
+ title: i18n.translate('visTypeTable.tableVisEditorConfig.schemas.splitTitle', {
+ defaultMessage: 'Split table',
+ }),
+ min: 0,
+ max: 1,
+ aggFilter: ['!filter'],
+ },
+ ]),
+ },
+ responseHandler: tableVisResponseHandler,
+ hierarchicalData: (vis: Vis) => {
+ return Boolean(vis.params.showPartialRows || vis.params.showMetricsAtAllLevels);
+ },
+ };
+}
diff --git a/src/legacy/core_plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts
similarity index 94%
rename from src/legacy/core_plugins/vis_type_table/public/types.ts
rename to src/plugins/vis_type_table/public/types.ts
index c6de14b9f050c..39023d1305cb6 100644
--- a/src/legacy/core_plugins/vis_type_table/public/types.ts
+++ b/src/plugins/vis_type_table/public/types.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { SchemaConfig } from '../../../../plugins/visualizations/public';
+import { SchemaConfig } from '../../visualizations/public';
export enum AggTypes {
SUM = 'sum',
diff --git a/src/plugins/vis_type_table/public/vis_controller.ts b/src/plugins/vis_type_table/public/vis_controller.ts
new file mode 100644
index 0000000000000..d49dd32c8c89c
--- /dev/null
+++ b/src/plugins/vis_type_table/public/vis_controller.ts
@@ -0,0 +1,109 @@
+/*
+ * 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 { CoreSetup, PluginInitializerContext } from 'kibana/public';
+import angular, { IModule, auto, IRootScopeService, IScope, ICompileService } from 'angular';
+import $ from 'jquery';
+
+import { VisParams, ExprVis } from '../../visualizations/public';
+import { getAngularModule } from './get_inner_angular';
+import { initTableVisLegacyModule } from './table_vis_legacy_module';
+
+const innerAngularName = 'kibana/table_vis';
+
+export function getTableVisualizationControllerClass(
+ core: CoreSetup,
+ context: PluginInitializerContext
+) {
+ return class TableVisualizationController {
+ private tableVisModule: IModule | undefined;
+ private injector: auto.IInjectorService | undefined;
+ el: JQuery;
+ vis: ExprVis;
+ $rootScope: IRootScopeService | null = null;
+ $scope: (IScope & { [key: string]: any }) | undefined;
+ $compile: ICompileService | undefined;
+
+ constructor(domeElement: Element, vis: ExprVis) {
+ this.el = $(domeElement);
+ this.vis = vis;
+ }
+
+ getInjector() {
+ if (!this.injector) {
+ const mountpoint = document.createElement('div');
+ mountpoint.setAttribute('style', 'height: 100%; width: 100%;');
+ this.injector = angular.bootstrap(mountpoint, [innerAngularName]);
+ this.el.append(mountpoint);
+ }
+
+ return this.injector;
+ }
+
+ async initLocalAngular() {
+ if (!this.tableVisModule) {
+ const [coreStart] = await core.getStartServices();
+ this.tableVisModule = getAngularModule(innerAngularName, coreStart, context);
+ initTableVisLegacyModule(this.tableVisModule);
+ }
+ }
+
+ async render(esResponse: object, visParams: VisParams) {
+ await this.initLocalAngular();
+
+ return new Promise(async (resolve, reject) => {
+ if (!this.$rootScope) {
+ const $injector = this.getInjector();
+ this.$rootScope = $injector.get('$rootScope');
+ this.$compile = $injector.get('$compile');
+ }
+ const updateScope = () => {
+ if (!this.$scope) {
+ return;
+ }
+ this.$scope.vis = this.vis;
+ this.$scope.visState = { params: visParams };
+ this.$scope.esResponse = esResponse;
+
+ this.$scope.visParams = visParams;
+ this.$scope.renderComplete = resolve;
+ this.$scope.renderFailed = reject;
+ this.$scope.resize = Date.now();
+ this.$scope.$apply();
+ };
+
+ if (!this.$scope && this.$compile) {
+ this.$scope = this.$rootScope.$new();
+ this.$scope.uiState = this.vis.getUiState();
+ updateScope();
+ this.el.find('div').append(this.$compile(this.vis.type!.visConfig.template)(this.$scope));
+ this.$scope.$apply();
+ } else {
+ updateScope();
+ }
+ });
+ }
+
+ destroy() {
+ if (this.$rootScope) {
+ this.$rootScope.$destroy();
+ this.$rootScope = null;
+ }
+ }
+ };
+}
diff --git a/src/plugins/timelion/public/index.ts b/src/plugins/vis_type_table/server/index.ts
similarity index 69%
rename from src/plugins/timelion/public/index.ts
rename to src/plugins/vis_type_table/server/index.ts
index b05c4f8a30b22..882958a28777d 100644
--- a/src/plugins/timelion/public/index.ts
+++ b/src/plugins/vis_type_table/server/index.ts
@@ -17,14 +17,18 @@
* under the License.
*/
-import { CoreStart, PluginInitializerContext } from 'kibana/public';
-import { ConfigSchema } from '../config';
+import { PluginConfigDescriptor } from 'kibana/server';
-export const plugin = (initializerContext: PluginInitializerContext) => ({
+import { configSchema, ConfigSchema } from '../config';
+
+export const config: PluginConfigDescriptor = {
+ schema: configSchema,
+ deprecations: ({ renameFromRoot }) => [
+ renameFromRoot('table_vis.enabled', 'vis_type_table.enabled'),
+ ],
+};
+
+export const plugin = () => ({
setup() {},
- start(core: CoreStart) {
- if (initializerContext.config.get().ui.enabled === false) {
- core.chrome.navLinks.update('timelion', { hidden: true });
- }
- },
+ start() {},
});
diff --git a/src/legacy/core_plugins/vis_type_timelion/README.md b/src/plugins/vis_type_timelion/README.md
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/README.md
rename to src/plugins/vis_type_timelion/README.md
diff --git a/src/plugins/timelion/common/chain.peg b/src/plugins/vis_type_timelion/common/chain.peg
similarity index 100%
rename from src/plugins/timelion/common/chain.peg
rename to src/plugins/vis_type_timelion/common/chain.peg
diff --git a/src/plugins/timelion/common/lib/calculate_interval.test.ts b/src/plugins/vis_type_timelion/common/lib/calculate_interval.test.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/calculate_interval.test.ts
rename to src/plugins/vis_type_timelion/common/lib/calculate_interval.test.ts
diff --git a/src/plugins/timelion/common/lib/calculate_interval.ts b/src/plugins/vis_type_timelion/common/lib/calculate_interval.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/calculate_interval.ts
rename to src/plugins/vis_type_timelion/common/lib/calculate_interval.ts
diff --git a/src/plugins/timelion/common/lib/index.ts b/src/plugins/vis_type_timelion/common/lib/index.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/index.ts
rename to src/plugins/vis_type_timelion/common/lib/index.ts
diff --git a/src/plugins/timelion/common/lib/to_milliseconds.ts b/src/plugins/vis_type_timelion/common/lib/to_milliseconds.ts
similarity index 100%
rename from src/plugins/timelion/common/lib/to_milliseconds.ts
rename to src/plugins/vis_type_timelion/common/lib/to_milliseconds.ts
diff --git a/src/plugins/timelion/common/types.ts b/src/plugins/vis_type_timelion/common/types.ts
similarity index 100%
rename from src/plugins/timelion/common/types.ts
rename to src/plugins/vis_type_timelion/common/types.ts
diff --git a/src/plugins/timelion/config.ts b/src/plugins/vis_type_timelion/config.ts
similarity index 100%
rename from src/plugins/timelion/config.ts
rename to src/plugins/vis_type_timelion/config.ts
diff --git a/src/plugins/vis_type_timelion/kibana.json b/src/plugins/vis_type_timelion/kibana.json
new file mode 100644
index 0000000000000..85c282c51a2e7
--- /dev/null
+++ b/src/plugins/vis_type_timelion/kibana.json
@@ -0,0 +1,8 @@
+{
+ "id": "visTypeTimelion",
+ "version": "8.0.0",
+ "kibanaVersion": "kibana",
+ "server": true,
+ "ui": true,
+ "requiredPlugins": ["visualizations", "data", "expressions"]
+}
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js b/src/plugins/vis_type_timelion/public/_generated_/chain.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js
rename to src/plugins/vis_type_timelion/public/_generated_/chain.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/_timelion_editor.scss b/src/plugins/vis_type_timelion/public/_timelion_editor.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/_timelion_editor.scss
rename to src/plugins/vis_type_timelion/public/_timelion_editor.scss
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/_timelion_vis.scss b/src/plugins/vis_type_timelion/public/_timelion_vis.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/_timelion_vis.scss
rename to src/plugins/vis_type_timelion/public/_timelion_vis.scss
diff --git a/src/plugins/vis_type_timelion/public/components/_index.scss b/src/plugins/vis_type_timelion/public/components/_index.scss
new file mode 100644
index 0000000000000..707c9dafebe2b
--- /dev/null
+++ b/src/plugins/vis_type_timelion/public/components/_index.scss
@@ -0,0 +1,2 @@
+@import 'panel';
+@import 'timelion_expression_input';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/_panel.scss b/src/plugins/vis_type_timelion/public/components/_panel.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/_panel.scss
rename to src/plugins/vis_type_timelion/public/components/_panel.scss
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/_timelion_expression_input.scss b/src/plugins/vis_type_timelion/public/components/_timelion_expression_input.scss
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/_timelion_expression_input.scss
rename to src/plugins/vis_type_timelion/public/components/_timelion_expression_input.scss
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/chart.tsx b/src/plugins/vis_type_timelion/public/components/chart.tsx
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/chart.tsx
rename to src/plugins/vis_type_timelion/public/components/chart.tsx
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/index.ts b/src/plugins/vis_type_timelion/public/components/index.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/index.ts
rename to src/plugins/vis_type_timelion/public/components/index.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/panel.tsx b/src/plugins/vis_type_timelion/public/components/panel.tsx
similarity index 98%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/panel.tsx
rename to src/plugins/vis_type_timelion/public/components/panel.tsx
index 3b42fa7dfcbb8..8f796526e8520 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/panel.tsx
+++ b/src/plugins/vis_type_timelion/public/components/panel.tsx
@@ -22,9 +22,9 @@ import $ from 'jquery';
import moment from 'moment-timezone';
import { debounce, compact, get, each, cloneDeep, last, map } from 'lodash';
-import { useKibana } from '../../../../../plugins/kibana_react/public';
+import { useKibana } from '../../../kibana_react/public';
import '../flot';
-import { DEFAULT_TIME_FORMAT } from '../../../../../plugins/timelion/common/lib';
+import { DEFAULT_TIME_FORMAT } from '../../common/lib';
import {
buildSeriesData,
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx b/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
similarity index 96%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
rename to src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
index c317451b8201e..999409ef35063 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_expression_input.tsx
@@ -22,13 +22,10 @@ import { EuiFormLabel } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { monaco } from '@kbn/ui-shared-deps/monaco';
-import { CodeEditor, useKibana } from '../../../../../plugins/kibana_react/public';
+import { CodeEditor, useKibana } from '../../../kibana_react/public';
import { suggest, getSuggestion } from './timelion_expression_input_helpers';
import { getArgValueSuggestions } from '../helpers/arg_value_suggestions';
-import {
- ITimelionFunction,
- TimelionFunctionArgs,
-} from '../../../../../plugins/timelion/common/types';
+import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
const LANGUAGE_ID = 'timelion_expression';
monaco.languages.register({ id: LANGUAGE_ID });
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
similarity index 99%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
rename to src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
index 2f99256e2a192..2ff6809d1c83d 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
+++ b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.test.ts
@@ -22,7 +22,7 @@ import { getArgValueSuggestions } from '../helpers/arg_value_suggestions';
import { setIndexPatterns, setSavedObjectsClient } from '../helpers/plugin_services';
import { IndexPatternsContract } from 'src/plugins/data/public';
import { SavedObjectsClient } from 'kibana/public';
-import { ITimelionFunction } from '../../../../../plugins/timelion/common/types';
+import { ITimelionFunction } from '../../common/types';
describe('Timelion expression suggestions', () => {
setIndexPatterns({} as IndexPatternsContract);
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
similarity index 98%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
rename to src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
index 6f23c864419eb..04cb54306c90e 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
+++ b/src/plugins/vis_type_timelion/public/components/timelion_expression_input_helpers.ts
@@ -27,10 +27,7 @@ import { Parser } from 'pegjs';
import { parse } from '../_generated_/chain';
import { ArgValueSuggestions, FunctionArg, Location } from '../helpers/arg_value_suggestions';
-import {
- ITimelionFunction,
- TimelionFunctionArgs,
-} from '../../../../../plugins/timelion/common/types';
+import { ITimelionFunction, TimelionFunctionArgs } from '../../common/types';
export enum SUGGESTION_TYPE {
ARGUMENTS = 'arguments',
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
similarity index 96%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx
rename to src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
index 8a8e1b22fb78d..985ecaeaf3e5a 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_interval.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
@@ -21,9 +21,9 @@ import React, { useMemo, useCallback } from 'react';
import { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
-import { search } from '../../../../../plugins/data/public';
+import { search } from '../../../data/public';
const { isValidEsInterval } = search.aggs;
-import { useValidation } from '../../../../../plugins/vis_default_editor/public';
+import { useValidation } from '../../../vis_default_editor/public';
const intervalOptions = [
{
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx b/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
similarity index 95%
rename from src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx
rename to src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
index 0fad0a164bf0b..4bb07fe74ee82 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/components/timelion_vis.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_vis.tsx
@@ -23,7 +23,7 @@ import { IUiSettingsClient } from 'kibana/public';
import { ChartComponent } from './chart';
import { VisParams } from '../timelion_vis_fn';
import { TimelionSuccessResponse } from '../helpers/timelion_request_handler';
-import { ExprVis } from '../../../../../plugins/visualizations/public';
+import { ExprVis } from '../../../visualizations/public';
export interface TimelionVisComponentProp {
config: IUiSettingsClient;
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/flot.js b/src/plugins/vis_type_timelion/public/flot.js
similarity index 72%
rename from src/legacy/core_plugins/vis_type_timelion/public/flot.js
rename to src/plugins/vis_type_timelion/public/flot.js
index d6ca6d96c34ef..1ccb40c93a3d6 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/flot.js
+++ b/src/plugins/vis_type_timelion/public/flot.js
@@ -17,10 +17,10 @@
* under the License.
*/
-require('jquery.flot');
-require('jquery.flot.time');
-require('jquery.flot.symbol');
-require('jquery.flot.crosshair');
-require('jquery.flot.selection');
-require('jquery.flot.stack');
-require('jquery.flot.axislabels');
+import './webpackShims/jquery.flot';
+import './webpackShims/jquery.flot.time';
+import './webpackShims/jquery.flot.symbol';
+import './webpackShims/jquery.flot.crosshair';
+import './webpackShims/jquery.flot.selection';
+import './webpackShims/jquery.flot.stack';
+import './webpackShims/jquery.flot.axislabels';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts b/src/plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
similarity index 97%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
rename to src/plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
index ea9532964d6fe..76c25b9b9e8de 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/arg_value_suggestions.ts
@@ -19,11 +19,8 @@
import { get } from 'lodash';
import { getIndexPatterns, getSavedObjectsClient } from './plugin_services';
-import { TimelionFunctionArgs } from '../../../../../plugins/timelion/common/types';
-import {
- indexPatterns as indexPatternsUtils,
- IndexPatternAttributes,
-} from '../../../../../plugins/data/public';
+import { TimelionFunctionArgs } from '../../common/types';
+import { indexPatterns as indexPatternsUtils, IndexPatternAttributes } from '../../../data/public';
export interface Location {
min: number;
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/get_timezone.ts b/src/plugins/vis_type_timelion/public/helpers/get_timezone.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/get_timezone.ts
rename to src/plugins/vis_type_timelion/public/helpers/get_timezone.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/panel_utils.ts b/src/plugins/vis_type_timelion/public/helpers/panel_utils.ts
similarity index 98%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/panel_utils.ts
rename to src/plugins/vis_type_timelion/public/helpers/panel_utils.ts
index f932e5ee4b2f4..db29d9112be8e 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/panel_utils.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/panel_utils.ts
@@ -23,7 +23,7 @@ import moment, { Moment } from 'moment-timezone';
import { TimefilterContract } from 'src/plugins/data/public';
import { IUiSettingsClient } from 'kibana/public';
-import { calculateInterval } from '../../../../../plugins/timelion/common/lib';
+import { calculateInterval } from '../../common/lib';
import { xaxisFormatterProvider } from './xaxis_formatter';
import { Series } from './timelion_request_handler';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/plugin_services.ts b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
similarity index 93%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/plugin_services.ts
rename to src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
index 5ba4ee5e47983..b055626934eea 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/plugin_services.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/plugin_services.ts
@@ -19,7 +19,7 @@
import { IndexPatternsContract } from 'src/plugins/data/public';
import { SavedObjectsClientContract } from 'kibana/public';
-import { createGetterSetter } from '../../../../../plugins/kibana_utils/public';
+import { createGetterSetter } from '../../../kibana_utils/public';
export const [getIndexPatterns, setIndexPatterns] = createGetterSetter(
'IndexPatterns'
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts b/src/plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_formatters.test.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.ts b/src/plugins/vis_type_timelion/public/helpers/tick_formatters.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_formatters.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_formatters.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.test.ts b/src/plugins/vis_type_timelion/public/helpers/tick_generator.test.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.test.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_generator.test.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.ts b/src/plugins/vis_type_timelion/public/helpers/tick_generator.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/tick_generator.ts
rename to src/plugins/vis_type_timelion/public/helpers/tick_generator.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
similarity index 95%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
rename to src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
index 61e31420f73ba..a654f7935af5f 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
+++ b/src/plugins/vis_type_timelion/public/helpers/timelion_request_handler.ts
@@ -19,8 +19,8 @@
import { i18n } from '@kbn/i18n';
import { KIBANA_CONTEXT_NAME } from 'src/plugins/expressions/public';
-import { VisParams } from '../../../../../plugins/visualizations/public';
-import { TimeRange, Filter, esQuery, Query } from '../../../../../plugins/data/public';
+import { VisParams } from '../../../visualizations/public';
+import { TimeRange, Filter, esQuery, Query } from '../../../data/public';
import { TimelionVisDependencies } from '../plugin';
import { getTimezone } from './get_timezone';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts b/src/plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts
rename to src/plugins/vis_type_timelion/public/helpers/xaxis_formatter.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/index.scss b/src/plugins/vis_type_timelion/public/index.scss
similarity index 59%
rename from src/legacy/core_plugins/vis_type_timelion/public/index.scss
rename to src/plugins/vis_type_timelion/public/index.scss
index 313f14a8acf69..00e9a88520961 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/index.scss
+++ b/src/plugins/vis_type_timelion/public/index.scss
@@ -1,5 +1,3 @@
-@import 'src/legacy/ui/public/styles/styling_constants';
-
@import './timelion_vis';
@import './timelion_editor';
@import './components/index';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/index.ts b/src/plugins/vis_type_timelion/public/index.ts
similarity index 90%
rename from src/legacy/core_plugins/vis_type_timelion/public/index.ts
rename to src/plugins/vis_type_timelion/public/index.ts
index 6292e2ad3eb08..0aa5f3a810033 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/index.ts
+++ b/src/plugins/vis_type_timelion/public/index.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../core/public';
+import { PluginInitializerContext } from 'kibana/public';
import { TimelionVisPlugin as Plugin } from './plugin';
export function plugin(initializerContext: PluginInitializerContext) {
@@ -25,3 +25,5 @@ export function plugin(initializerContext: PluginInitializerContext) {
}
export { getTimezone } from './helpers/get_timezone';
+
+export { VisTypeTimelionPluginStart } from './plugin';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts b/src/plugins/vis_type_timelion/public/plugin.ts
similarity index 67%
rename from src/legacy/core_plugins/vis_type_timelion/public/plugin.ts
rename to src/plugins/vis_type_timelion/public/plugin.ts
index b5aa64db19aa4..060fec04deb3f 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/plugin.ts
+++ b/src/plugins/vis_type_timelion/public/plugin.ts
@@ -26,16 +26,21 @@ import {
HttpSetup,
} from 'kibana/public';
import { Plugin as ExpressionsPlugin } from 'src/plugins/expressions/public';
-import { DataPublicPluginSetup, TimefilterContract } from 'src/plugins/data/public';
+import {
+ DataPublicPluginSetup,
+ DataPublicPluginStart,
+ TimefilterContract,
+} from 'src/plugins/data/public';
-import { PluginsStart } from './legacy_imports';
-import { VisualizationsSetup } from '../../../../plugins/visualizations/public';
+import { VisualizationsSetup } from '../../visualizations/public';
import { getTimelionVisualizationConfig } from './timelion_vis_fn';
import { getTimelionVisDefinition } from './timelion_vis_type';
import { setIndexPatterns, setSavedObjectsClient } from './helpers/plugin_services';
+import { ConfigSchema } from '../config';
-type TimelionVisCoreSetup = CoreSetup;
+import './index.scss';
+import { getArgValueSuggestions } from './helpers/arg_value_suggestions';
/** @internal */
export interface TimelionVisDependencies extends Partial {
@@ -52,11 +57,28 @@ export interface TimelionVisSetupDependencies {
}
/** @internal */
-export class TimelionVisPlugin implements Plugin {
- constructor(public initializerContext: PluginInitializerContext) {}
+export interface TimelionVisStartDependencies {
+ data: DataPublicPluginStart;
+}
+
+/** @public */
+export interface VisTypeTimelionPluginStart {
+ getArgValueSuggestions: typeof getArgValueSuggestions;
+}
+
+/** @internal */
+export class TimelionVisPlugin
+ implements
+ Plugin<
+ void,
+ VisTypeTimelionPluginStart,
+ TimelionVisSetupDependencies,
+ TimelionVisStartDependencies
+ > {
+ constructor(public initializerContext: PluginInitializerContext) {}
- public async setup(
- core: TimelionVisCoreSetup,
+ public setup(
+ core: CoreSetup,
{ expressions, visualizations, data }: TimelionVisSetupDependencies
) {
const dependencies: TimelionVisDependencies = {
@@ -69,8 +91,15 @@ export class TimelionVisPlugin implements Plugin {
visualizations.createReactVisualization(getTimelionVisDefinition(dependencies));
}
- public start(core: CoreStart, plugins: PluginsStart) {
+ public start(core: CoreStart, plugins: TimelionVisStartDependencies) {
setIndexPatterns(plugins.data.indexPatterns);
setSavedObjectsClient(core.savedObjects.client);
+ if (this.initializerContext.config.get().ui.enabled === false) {
+ core.chrome.navLinks.update('timelion', { hidden: true });
+ }
+
+ return {
+ getArgValueSuggestions,
+ };
}
}
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx b/src/plugins/vis_type_timelion/public/timelion_options.tsx
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/timelion_options.tsx
rename to src/plugins/vis_type_timelion/public/timelion_options.tsx
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_fn.ts b/src/plugins/vis_type_timelion/public/timelion_vis_fn.ts
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_fn.ts
rename to src/plugins/vis_type_timelion/public/timelion_vis_fn.ts
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
similarity index 93%
rename from src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx
rename to src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
index 0900b7d898ede..52addb3c2d9d2 100644
--- a/src/legacy/core_plugins/vis_type_timelion/public/timelion_vis_type.tsx
+++ b/src/plugins/vis_type_timelion/public/timelion_vis_type.tsx
@@ -20,8 +20,8 @@
import React from 'react';
import { i18n } from '@kbn/i18n';
-import { KibanaContextProvider } from '../../../../plugins/kibana_react/public';
-import { DefaultEditorSize } from '../../../../plugins/vis_default_editor/public';
+import { KibanaContextProvider } from '../../kibana_react/public';
+import { DefaultEditorSize } from '../../vis_default_editor/public';
import { getTimelionRequestHandler } from './helpers/timelion_request_handler';
import { TimelionVisComponent, TimelionVisComponentProp } from './components';
import { TimelionOptions, TimelionOptionsProps } from './timelion_options';
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.axislabels.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.crosshair.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.selection.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.stack.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.symbol.js
diff --git a/src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js b/src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js
similarity index 100%
rename from src/legacy/core_plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js
rename to src/plugins/vis_type_timelion/public/webpackShims/jquery.flot.time.js
diff --git a/src/plugins/timelion/server/fit_functions/average.js b/src/plugins/vis_type_timelion/server/fit_functions/average.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/average.js
rename to src/plugins/vis_type_timelion/server/fit_functions/average.js
diff --git a/src/plugins/timelion/server/fit_functions/average.test.js b/src/plugins/vis_type_timelion/server/fit_functions/average.test.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/average.test.js
rename to src/plugins/vis_type_timelion/server/fit_functions/average.test.js
diff --git a/src/plugins/timelion/server/fit_functions/carry.js b/src/plugins/vis_type_timelion/server/fit_functions/carry.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/carry.js
rename to src/plugins/vis_type_timelion/server/fit_functions/carry.js
diff --git a/src/plugins/timelion/server/fit_functions/carry.test.js b/src/plugins/vis_type_timelion/server/fit_functions/carry.test.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/carry.test.js
rename to src/plugins/vis_type_timelion/server/fit_functions/carry.test.js
diff --git a/src/plugins/timelion/server/fit_functions/nearest.js b/src/plugins/vis_type_timelion/server/fit_functions/nearest.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/nearest.js
rename to src/plugins/vis_type_timelion/server/fit_functions/nearest.js
diff --git a/src/plugins/timelion/server/fit_functions/none.js b/src/plugins/vis_type_timelion/server/fit_functions/none.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/none.js
rename to src/plugins/vis_type_timelion/server/fit_functions/none.js
diff --git a/src/plugins/timelion/server/fit_functions/scale.js b/src/plugins/vis_type_timelion/server/fit_functions/scale.js
similarity index 100%
rename from src/plugins/timelion/server/fit_functions/scale.js
rename to src/plugins/vis_type_timelion/server/fit_functions/scale.js
diff --git a/src/plugins/timelion/server/handlers/chain_runner.js b/src/plugins/vis_type_timelion/server/handlers/chain_runner.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/chain_runner.js
rename to src/plugins/vis_type_timelion/server/handlers/chain_runner.js
diff --git a/src/plugins/timelion/server/handlers/lib/arg_type.js b/src/plugins/vis_type_timelion/server/handlers/lib/arg_type.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/arg_type.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/arg_type.js
diff --git a/src/plugins/timelion/server/handlers/lib/index_arguments.js b/src/plugins/vis_type_timelion/server/handlers/lib/index_arguments.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/index_arguments.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/index_arguments.js
diff --git a/src/plugins/timelion/server/handlers/lib/parse_sheet.js b/src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/parse_sheet.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.js
diff --git a/src/plugins/timelion/server/handlers/lib/parse_sheet.test.js b/src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.test.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/parse_sheet.test.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/parse_sheet.test.js
diff --git a/src/plugins/timelion/server/handlers/lib/preprocess_chain.js b/src/plugins/vis_type_timelion/server/handlers/lib/preprocess_chain.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/preprocess_chain.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/preprocess_chain.js
diff --git a/src/plugins/timelion/server/handlers/lib/reposition_arguments.js b/src/plugins/vis_type_timelion/server/handlers/lib/reposition_arguments.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/reposition_arguments.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/reposition_arguments.js
diff --git a/src/plugins/timelion/server/handlers/lib/tl_config.js b/src/plugins/vis_type_timelion/server/handlers/lib/tl_config.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/tl_config.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/tl_config.js
diff --git a/src/plugins/timelion/server/handlers/lib/validate_arg.js b/src/plugins/vis_type_timelion/server/handlers/lib/validate_arg.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/validate_arg.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/validate_arg.js
diff --git a/src/plugins/timelion/server/handlers/lib/validate_time.js b/src/plugins/vis_type_timelion/server/handlers/lib/validate_time.js
similarity index 100%
rename from src/plugins/timelion/server/handlers/lib/validate_time.js
rename to src/plugins/vis_type_timelion/server/handlers/lib/validate_time.js
diff --git a/src/plugins/timelion/server/index.ts b/src/plugins/vis_type_timelion/server/index.ts
similarity index 64%
rename from src/plugins/timelion/server/index.ts
rename to src/plugins/vis_type_timelion/server/index.ts
index 5d420327f961e..b40ab2af2b0d7 100644
--- a/src/plugins/timelion/server/index.ts
+++ b/src/plugins/vis_type_timelion/server/index.ts
@@ -17,19 +17,23 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../src/core/server';
-import { configSchema } from '../config';
+import { PluginConfigDescriptor, PluginInitializerContext } from '../../../../src/core/server';
+import { configSchema, ConfigSchema } from '../config';
import { Plugin } from './plugin';
export { PluginSetupContract } from './plugin';
-export const config = {
+export const config: PluginConfigDescriptor = {
schema: configSchema,
exposeToBrowser: {
- ui: {
- enabled: true,
- },
+ ui: true,
},
+ deprecations: ({ renameFromRoot }) => [
+ renameFromRoot('timelion_vis.enabled', 'vis_type_timelion.enabled'),
+ renameFromRoot('timelion.enabled', 'vis_type_timelion.enabled'),
+ renameFromRoot('timelion.graphiteUrls', 'vis_type_timelion.graphiteUrls'),
+ renameFromRoot('timelion.ui.enabled', 'vis_type_timelion.ui.enabled'),
+ ],
};
export const plugin = (initializerContext: PluginInitializerContext) =>
new Plugin(initializerContext);
diff --git a/src/plugins/timelion/server/lib/alter.js b/src/plugins/vis_type_timelion/server/lib/alter.js
similarity index 100%
rename from src/plugins/timelion/server/lib/alter.js
rename to src/plugins/vis_type_timelion/server/lib/alter.js
diff --git a/src/plugins/timelion/server/lib/as_sorted.js b/src/plugins/vis_type_timelion/server/lib/as_sorted.js
similarity index 100%
rename from src/plugins/timelion/server/lib/as_sorted.js
rename to src/plugins/vis_type_timelion/server/lib/as_sorted.js
diff --git a/src/plugins/timelion/server/lib/build_target.js b/src/plugins/vis_type_timelion/server/lib/build_target.js
similarity index 100%
rename from src/plugins/timelion/server/lib/build_target.js
rename to src/plugins/vis_type_timelion/server/lib/build_target.js
diff --git a/src/plugins/timelion/server/lib/classes/chainable.js b/src/plugins/vis_type_timelion/server/lib/classes/chainable.js
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/chainable.js
rename to src/plugins/vis_type_timelion/server/lib/classes/chainable.js
diff --git a/src/plugins/timelion/server/lib/classes/datasource.js b/src/plugins/vis_type_timelion/server/lib/classes/datasource.js
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/datasource.js
rename to src/plugins/vis_type_timelion/server/lib/classes/datasource.js
diff --git a/src/plugins/timelion/server/lib/classes/timelion_function.d.ts b/src/plugins/vis_type_timelion/server/lib/classes/timelion_function.d.ts
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/timelion_function.d.ts
rename to src/plugins/vis_type_timelion/server/lib/classes/timelion_function.d.ts
diff --git a/src/plugins/timelion/server/lib/classes/timelion_function.js b/src/plugins/vis_type_timelion/server/lib/classes/timelion_function.js
similarity index 100%
rename from src/plugins/timelion/server/lib/classes/timelion_function.js
rename to src/plugins/vis_type_timelion/server/lib/classes/timelion_function.js
diff --git a/src/plugins/timelion/server/lib/config_manager.ts b/src/plugins/vis_type_timelion/server/lib/config_manager.ts
similarity index 100%
rename from src/plugins/timelion/server/lib/config_manager.ts
rename to src/plugins/vis_type_timelion/server/lib/config_manager.ts
diff --git a/src/plugins/timelion/server/lib/functions_md.js b/src/plugins/vis_type_timelion/server/lib/functions_md.js
similarity index 100%
rename from src/plugins/timelion/server/lib/functions_md.js
rename to src/plugins/vis_type_timelion/server/lib/functions_md.js
diff --git a/src/plugins/timelion/server/lib/get_namespaced_settings.js b/src/plugins/vis_type_timelion/server/lib/get_namespaced_settings.js
similarity index 100%
rename from src/plugins/timelion/server/lib/get_namespaced_settings.js
rename to src/plugins/vis_type_timelion/server/lib/get_namespaced_settings.js
diff --git a/src/plugins/timelion/server/lib/load_functions.d.ts b/src/plugins/vis_type_timelion/server/lib/load_functions.d.ts
similarity index 100%
rename from src/plugins/timelion/server/lib/load_functions.d.ts
rename to src/plugins/vis_type_timelion/server/lib/load_functions.d.ts
diff --git a/src/plugins/timelion/server/lib/load_functions.js b/src/plugins/vis_type_timelion/server/lib/load_functions.js
similarity index 100%
rename from src/plugins/timelion/server/lib/load_functions.js
rename to src/plugins/vis_type_timelion/server/lib/load_functions.js
diff --git a/src/plugins/timelion/server/lib/load_functions.test.js b/src/plugins/vis_type_timelion/server/lib/load_functions.test.js
similarity index 94%
rename from src/plugins/timelion/server/lib/load_functions.test.js
rename to src/plugins/vis_type_timelion/server/lib/load_functions.test.js
index ebe1a04532e05..b4f83611a7773 100644
--- a/src/plugins/timelion/server/lib/load_functions.test.js
+++ b/src/plugins/vis_type_timelion/server/lib/load_functions.test.js
@@ -17,7 +17,7 @@
* under the License.
*/
-const fn = require(`src/plugins/timelion/server/lib/load_functions`);
+const fn = require(`src/plugins/vis_type_timelion/server/lib/load_functions`);
const expect = require('chai').expect;
diff --git a/src/plugins/timelion/server/lib/offset_time.js b/src/plugins/vis_type_timelion/server/lib/offset_time.js
similarity index 100%
rename from src/plugins/timelion/server/lib/offset_time.js
rename to src/plugins/vis_type_timelion/server/lib/offset_time.js
diff --git a/src/plugins/timelion/server/lib/offset_time.test.js b/src/plugins/vis_type_timelion/server/lib/offset_time.test.js
similarity index 100%
rename from src/plugins/timelion/server/lib/offset_time.test.js
rename to src/plugins/vis_type_timelion/server/lib/offset_time.test.js
diff --git a/src/plugins/timelion/server/lib/process_function_definition.js b/src/plugins/vis_type_timelion/server/lib/process_function_definition.js
similarity index 100%
rename from src/plugins/timelion/server/lib/process_function_definition.js
rename to src/plugins/vis_type_timelion/server/lib/process_function_definition.js
diff --git a/src/plugins/timelion/server/lib/reduce.js b/src/plugins/vis_type_timelion/server/lib/reduce.js
similarity index 100%
rename from src/plugins/timelion/server/lib/reduce.js
rename to src/plugins/vis_type_timelion/server/lib/reduce.js
diff --git a/src/plugins/timelion/server/lib/split_interval.js b/src/plugins/vis_type_timelion/server/lib/split_interval.js
similarity index 100%
rename from src/plugins/timelion/server/lib/split_interval.js
rename to src/plugins/vis_type_timelion/server/lib/split_interval.js
diff --git a/src/plugins/timelion/server/lib/unzip_pairs.js b/src/plugins/vis_type_timelion/server/lib/unzip_pairs.js
similarity index 100%
rename from src/plugins/timelion/server/lib/unzip_pairs.js
rename to src/plugins/vis_type_timelion/server/lib/unzip_pairs.js
diff --git a/src/plugins/timelion/server/plugin.ts b/src/plugins/vis_type_timelion/server/plugin.ts
similarity index 100%
rename from src/plugins/timelion/server/plugin.ts
rename to src/plugins/vis_type_timelion/server/plugin.ts
diff --git a/src/plugins/timelion/server/routes/functions.ts b/src/plugins/vis_type_timelion/server/routes/functions.ts
similarity index 100%
rename from src/plugins/timelion/server/routes/functions.ts
rename to src/plugins/vis_type_timelion/server/routes/functions.ts
diff --git a/src/plugins/timelion/server/routes/run.ts b/src/plugins/vis_type_timelion/server/routes/run.ts
similarity index 100%
rename from src/plugins/timelion/server/routes/run.ts
rename to src/plugins/vis_type_timelion/server/routes/run.ts
diff --git a/src/plugins/timelion/server/routes/validate_es.ts b/src/plugins/vis_type_timelion/server/routes/validate_es.ts
similarity index 100%
rename from src/plugins/timelion/server/routes/validate_es.ts
rename to src/plugins/vis_type_timelion/server/routes/validate_es.ts
diff --git a/src/plugins/timelion/server/series_functions/abs.js b/src/plugins/vis_type_timelion/server/series_functions/abs.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/abs.js
rename to src/plugins/vis_type_timelion/server/series_functions/abs.js
diff --git a/src/plugins/timelion/server/series_functions/abs.test.js b/src/plugins/vis_type_timelion/server/series_functions/abs.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/abs.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/abs.test.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/aggregate.test.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/aggregate.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/aggregate.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/aggregate.test.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/avg.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/avg.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/avg.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/avg.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/cardinality.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/cardinality.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/cardinality.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/cardinality.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/first.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/first.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/first.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/first.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/index.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/index.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/last.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/last.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/last.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/last.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/max.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/max.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/max.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/max.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/min.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/min.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/min.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/min.js
diff --git a/src/plugins/timelion/server/series_functions/aggregate/sum.js b/src/plugins/vis_type_timelion/server/series_functions/aggregate/sum.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/aggregate/sum.js
rename to src/plugins/vis_type_timelion/server/series_functions/aggregate/sum.js
diff --git a/src/plugins/timelion/server/series_functions/bars.js b/src/plugins/vis_type_timelion/server/series_functions/bars.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/bars.js
rename to src/plugins/vis_type_timelion/server/series_functions/bars.js
diff --git a/src/plugins/timelion/server/series_functions/bars.test.js b/src/plugins/vis_type_timelion/server/series_functions/bars.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/bars.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/bars.test.js
diff --git a/src/plugins/timelion/server/series_functions/color.js b/src/plugins/vis_type_timelion/server/series_functions/color.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/color.js
rename to src/plugins/vis_type_timelion/server/series_functions/color.js
diff --git a/src/plugins/timelion/server/series_functions/color.test.js b/src/plugins/vis_type_timelion/server/series_functions/color.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/color.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/color.test.js
diff --git a/src/plugins/timelion/server/series_functions/condition.js b/src/plugins/vis_type_timelion/server/series_functions/condition.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/condition.js
rename to src/plugins/vis_type_timelion/server/series_functions/condition.js
diff --git a/src/plugins/timelion/server/series_functions/condition.test.js b/src/plugins/vis_type_timelion/server/series_functions/condition.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/condition.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/condition.test.js
diff --git a/src/plugins/timelion/server/series_functions/cusum.js b/src/plugins/vis_type_timelion/server/series_functions/cusum.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/cusum.js
rename to src/plugins/vis_type_timelion/server/series_functions/cusum.js
diff --git a/src/plugins/timelion/server/series_functions/cusum.test.js b/src/plugins/vis_type_timelion/server/series_functions/cusum.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/cusum.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/cusum.test.js
diff --git a/src/plugins/timelion/server/series_functions/derivative.js b/src/plugins/vis_type_timelion/server/series_functions/derivative.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/derivative.js
rename to src/plugins/vis_type_timelion/server/series_functions/derivative.js
diff --git a/src/plugins/timelion/server/series_functions/derivative.test.js b/src/plugins/vis_type_timelion/server/series_functions/derivative.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/derivative.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/derivative.test.js
diff --git a/src/plugins/timelion/server/series_functions/divide.js b/src/plugins/vis_type_timelion/server/series_functions/divide.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/divide.js
rename to src/plugins/vis_type_timelion/server/series_functions/divide.js
diff --git a/src/plugins/timelion/server/series_functions/divide.test.js b/src/plugins/vis_type_timelion/server/series_functions/divide.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/divide.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/divide.test.js
diff --git a/src/plugins/timelion/server/series_functions/es/es.test.js b/src/plugins/vis_type_timelion/server/series_functions/es/es.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/es.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/es.test.js
diff --git a/src/plugins/timelion/server/series_functions/es/index.js b/src/plugins/vis_type_timelion/server/series_functions/es/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/index.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/agg_body.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_body.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/agg_body.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_body.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/agg_response_to_series_list.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_response_to_series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/agg_response_to_series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/agg_response_to_series_list.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/build_request.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/build_request.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/build_request.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/build_request.js
diff --git a/src/plugins/timelion/server/series_functions/es/lib/create_date_agg.js b/src/plugins/vis_type_timelion/server/series_functions/es/lib/create_date_agg.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/es/lib/create_date_agg.js
rename to src/plugins/vis_type_timelion/server/series_functions/es/lib/create_date_agg.js
diff --git a/src/plugins/timelion/server/series_functions/first.js b/src/plugins/vis_type_timelion/server/series_functions/first.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/first.js
rename to src/plugins/vis_type_timelion/server/series_functions/first.js
diff --git a/src/plugins/timelion/server/series_functions/first.test.js b/src/plugins/vis_type_timelion/server/series_functions/first.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/first.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/first.test.js
diff --git a/src/plugins/timelion/server/series_functions/fit.js b/src/plugins/vis_type_timelion/server/series_functions/fit.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fit.js
rename to src/plugins/vis_type_timelion/server/series_functions/fit.js
diff --git a/src/plugins/timelion/server/series_functions/fit.test.js b/src/plugins/vis_type_timelion/server/series_functions/fit.test.js
similarity index 98%
rename from src/plugins/timelion/server/series_functions/fit.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/fit.test.js
index 75eaa2a50ea72..6622259a1fd87 100644
--- a/src/plugins/timelion/server/series_functions/fit.test.js
+++ b/src/plugins/vis_type_timelion/server/series_functions/fit.test.js
@@ -17,7 +17,7 @@
* under the License.
*/
-const fn = require(`src/plugins/timelion/server/series_functions/fit`);
+const fn = require(`src/plugins/vis_type_timelion/server/series_functions/fit`);
import moment from 'moment';
const expect = require('chai').expect;
import invoke from './helpers/invoke_series_fn.js';
diff --git a/src/plugins/timelion/server/series_functions/fixtures/bucket_list.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/bucket_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/bucket_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/bucket_list.js
diff --git a/src/plugins/timelion/server/series_functions/fixtures/es_response.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/es_response.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/es_response.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/es_response.js
diff --git a/src/plugins/timelion/server/series_functions/fixtures/series_list.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/series_list.js
diff --git a/src/plugins/timelion/server/series_functions/fixtures/tl_config.js b/src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/fixtures/tl_config.js
rename to src/plugins/vis_type_timelion/server/series_functions/fixtures/tl_config.js
diff --git a/src/plugins/timelion/server/series_functions/graphite.js b/src/plugins/vis_type_timelion/server/series_functions/graphite.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/graphite.js
rename to src/plugins/vis_type_timelion/server/series_functions/graphite.js
diff --git a/src/plugins/timelion/server/series_functions/graphite.test.js b/src/plugins/vis_type_timelion/server/series_functions/graphite.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/graphite.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/graphite.test.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/get_series.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/get_series.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/get_series.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/get_series.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/get_series_list.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/get_series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/get_series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/get_series_list.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/get_single_series_list.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/get_single_series_list.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/get_single_series_list.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/get_single_series_list.js
diff --git a/src/plugins/timelion/server/series_functions/helpers/invoke_series_fn.js b/src/plugins/vis_type_timelion/server/series_functions/helpers/invoke_series_fn.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/helpers/invoke_series_fn.js
rename to src/plugins/vis_type_timelion/server/series_functions/helpers/invoke_series_fn.js
diff --git a/src/plugins/timelion/server/series_functions/hide.js b/src/plugins/vis_type_timelion/server/series_functions/hide.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/hide.js
rename to src/plugins/vis_type_timelion/server/series_functions/hide.js
diff --git a/src/plugins/timelion/server/series_functions/hide.test.js b/src/plugins/vis_type_timelion/server/series_functions/hide.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/hide.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/hide.test.js
diff --git a/src/plugins/timelion/server/series_functions/holt/index.js b/src/plugins/vis_type_timelion/server/series_functions/holt/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/index.js
diff --git a/src/plugins/timelion/server/series_functions/holt/lib/des.js b/src/plugins/vis_type_timelion/server/series_functions/holt/lib/des.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/lib/des.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/lib/des.js
diff --git a/src/plugins/timelion/server/series_functions/holt/lib/ses.js b/src/plugins/vis_type_timelion/server/series_functions/holt/lib/ses.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/lib/ses.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/lib/ses.js
diff --git a/src/plugins/timelion/server/series_functions/holt/lib/tes.js b/src/plugins/vis_type_timelion/server/series_functions/holt/lib/tes.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/holt/lib/tes.js
rename to src/plugins/vis_type_timelion/server/series_functions/holt/lib/tes.js
diff --git a/src/plugins/timelion/server/series_functions/label.js b/src/plugins/vis_type_timelion/server/series_functions/label.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/label.js
rename to src/plugins/vis_type_timelion/server/series_functions/label.js
diff --git a/src/plugins/timelion/server/series_functions/label.test.js b/src/plugins/vis_type_timelion/server/series_functions/label.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/label.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/label.test.js
diff --git a/src/plugins/timelion/server/series_functions/legend.js b/src/plugins/vis_type_timelion/server/series_functions/legend.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/legend.js
rename to src/plugins/vis_type_timelion/server/series_functions/legend.js
diff --git a/src/plugins/timelion/server/series_functions/legend.test.js b/src/plugins/vis_type_timelion/server/series_functions/legend.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/legend.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/legend.test.js
diff --git a/src/plugins/timelion/server/series_functions/lines.js b/src/plugins/vis_type_timelion/server/series_functions/lines.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/lines.js
rename to src/plugins/vis_type_timelion/server/series_functions/lines.js
diff --git a/src/plugins/timelion/server/series_functions/lines.test.js b/src/plugins/vis_type_timelion/server/series_functions/lines.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/lines.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/lines.test.js
diff --git a/src/plugins/timelion/server/series_functions/log.js b/src/plugins/vis_type_timelion/server/series_functions/log.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/log.js
rename to src/plugins/vis_type_timelion/server/series_functions/log.js
diff --git a/src/plugins/timelion/server/series_functions/log.test.js b/src/plugins/vis_type_timelion/server/series_functions/log.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/log.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/log.test.js
diff --git a/src/plugins/timelion/server/series_functions/max.js b/src/plugins/vis_type_timelion/server/series_functions/max.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/max.js
rename to src/plugins/vis_type_timelion/server/series_functions/max.js
diff --git a/src/plugins/timelion/server/series_functions/max.test.js b/src/plugins/vis_type_timelion/server/series_functions/max.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/max.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/max.test.js
diff --git a/src/plugins/timelion/server/series_functions/min.js b/src/plugins/vis_type_timelion/server/series_functions/min.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/min.js
rename to src/plugins/vis_type_timelion/server/series_functions/min.js
diff --git a/src/plugins/timelion/server/series_functions/min.test.js b/src/plugins/vis_type_timelion/server/series_functions/min.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/min.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/min.test.js
diff --git a/src/plugins/timelion/server/series_functions/movingaverage.js b/src/plugins/vis_type_timelion/server/series_functions/movingaverage.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingaverage.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingaverage.js
diff --git a/src/plugins/timelion/server/series_functions/movingaverage.test.js b/src/plugins/vis_type_timelion/server/series_functions/movingaverage.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingaverage.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingaverage.test.js
diff --git a/src/plugins/timelion/server/series_functions/movingstd.js b/src/plugins/vis_type_timelion/server/series_functions/movingstd.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingstd.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingstd.js
diff --git a/src/plugins/timelion/server/series_functions/movingstd.test.js b/src/plugins/vis_type_timelion/server/series_functions/movingstd.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/movingstd.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/movingstd.test.js
diff --git a/src/plugins/timelion/server/series_functions/multiply.js b/src/plugins/vis_type_timelion/server/series_functions/multiply.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/multiply.js
rename to src/plugins/vis_type_timelion/server/series_functions/multiply.js
diff --git a/src/plugins/timelion/server/series_functions/multiply.test.js b/src/plugins/vis_type_timelion/server/series_functions/multiply.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/multiply.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/multiply.test.js
diff --git a/src/plugins/timelion/server/series_functions/points.js b/src/plugins/vis_type_timelion/server/series_functions/points.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/points.js
rename to src/plugins/vis_type_timelion/server/series_functions/points.js
diff --git a/src/plugins/timelion/server/series_functions/points.test.js b/src/plugins/vis_type_timelion/server/series_functions/points.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/points.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/points.test.js
diff --git a/src/plugins/timelion/server/series_functions/precision.js b/src/plugins/vis_type_timelion/server/series_functions/precision.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/precision.js
rename to src/plugins/vis_type_timelion/server/series_functions/precision.js
diff --git a/src/plugins/timelion/server/series_functions/precision.test.js b/src/plugins/vis_type_timelion/server/series_functions/precision.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/precision.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/precision.test.js
diff --git a/src/plugins/timelion/server/series_functions/props.js b/src/plugins/vis_type_timelion/server/series_functions/props.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/props.js
rename to src/plugins/vis_type_timelion/server/series_functions/props.js
diff --git a/src/plugins/timelion/server/series_functions/quandl.js b/src/plugins/vis_type_timelion/server/series_functions/quandl.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/quandl.js
rename to src/plugins/vis_type_timelion/server/series_functions/quandl.js
diff --git a/src/plugins/timelion/server/series_functions/quandl.test.js b/src/plugins/vis_type_timelion/server/series_functions/quandl.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/quandl.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/quandl.test.js
diff --git a/src/plugins/timelion/server/series_functions/range.js b/src/plugins/vis_type_timelion/server/series_functions/range.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/range.js
rename to src/plugins/vis_type_timelion/server/series_functions/range.js
diff --git a/src/plugins/timelion/server/series_functions/range.test.js b/src/plugins/vis_type_timelion/server/series_functions/range.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/range.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/range.test.js
diff --git a/src/plugins/timelion/server/series_functions/scale_interval.js b/src/plugins/vis_type_timelion/server/series_functions/scale_interval.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/scale_interval.js
rename to src/plugins/vis_type_timelion/server/series_functions/scale_interval.js
diff --git a/src/plugins/timelion/server/series_functions/scale_interval.test.js b/src/plugins/vis_type_timelion/server/series_functions/scale_interval.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/scale_interval.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/scale_interval.test.js
diff --git a/src/plugins/timelion/server/series_functions/static.js b/src/plugins/vis_type_timelion/server/series_functions/static.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/static.js
rename to src/plugins/vis_type_timelion/server/series_functions/static.js
diff --git a/src/plugins/timelion/server/series_functions/static.test.js b/src/plugins/vis_type_timelion/server/series_functions/static.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/static.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/static.test.js
diff --git a/src/plugins/timelion/server/series_functions/subtract.js b/src/plugins/vis_type_timelion/server/series_functions/subtract.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/subtract.js
rename to src/plugins/vis_type_timelion/server/series_functions/subtract.js
diff --git a/src/plugins/timelion/server/series_functions/subtract.test.js b/src/plugins/vis_type_timelion/server/series_functions/subtract.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/subtract.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/subtract.test.js
diff --git a/src/plugins/timelion/server/series_functions/sum.js b/src/plugins/vis_type_timelion/server/series_functions/sum.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/sum.js
rename to src/plugins/vis_type_timelion/server/series_functions/sum.js
diff --git a/src/plugins/timelion/server/series_functions/sum.test.js b/src/plugins/vis_type_timelion/server/series_functions/sum.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/sum.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/sum.test.js
diff --git a/src/plugins/timelion/server/series_functions/title.js b/src/plugins/vis_type_timelion/server/series_functions/title.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/title.js
rename to src/plugins/vis_type_timelion/server/series_functions/title.js
diff --git a/src/plugins/timelion/server/series_functions/title.test.js b/src/plugins/vis_type_timelion/server/series_functions/title.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/title.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/title.test.js
diff --git a/src/plugins/timelion/server/series_functions/trend/index.js b/src/plugins/vis_type_timelion/server/series_functions/trend/index.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trend/index.js
rename to src/plugins/vis_type_timelion/server/series_functions/trend/index.js
diff --git a/src/plugins/timelion/server/series_functions/trend/lib/regress.js b/src/plugins/vis_type_timelion/server/series_functions/trend/lib/regress.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trend/lib/regress.js
rename to src/plugins/vis_type_timelion/server/series_functions/trend/lib/regress.js
diff --git a/src/plugins/timelion/server/series_functions/trim.js b/src/plugins/vis_type_timelion/server/series_functions/trim.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trim.js
rename to src/plugins/vis_type_timelion/server/series_functions/trim.js
diff --git a/src/plugins/timelion/server/series_functions/trim.test.js b/src/plugins/vis_type_timelion/server/series_functions/trim.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/trim.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/trim.test.js
diff --git a/src/plugins/timelion/server/series_functions/worldbank.js b/src/plugins/vis_type_timelion/server/series_functions/worldbank.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/worldbank.js
rename to src/plugins/vis_type_timelion/server/series_functions/worldbank.js
diff --git a/src/plugins/timelion/server/series_functions/worldbank_indicators.js b/src/plugins/vis_type_timelion/server/series_functions/worldbank_indicators.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/worldbank_indicators.js
rename to src/plugins/vis_type_timelion/server/series_functions/worldbank_indicators.js
diff --git a/src/plugins/timelion/server/series_functions/yaxis.js b/src/plugins/vis_type_timelion/server/series_functions/yaxis.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/yaxis.js
rename to src/plugins/vis_type_timelion/server/series_functions/yaxis.js
diff --git a/src/plugins/timelion/server/series_functions/yaxis.test.js b/src/plugins/vis_type_timelion/server/series_functions/yaxis.test.js
similarity index 100%
rename from src/plugins/timelion/server/series_functions/yaxis.test.js
rename to src/plugins/vis_type_timelion/server/series_functions/yaxis.test.js
diff --git a/src/plugins/timelion/server/timelion.json b/src/plugins/vis_type_timelion/server/timelion.json
similarity index 100%
rename from src/plugins/timelion/server/timelion.json
rename to src/plugins/vis_type_timelion/server/timelion.json
diff --git a/src/plugins/timelion/server/types.ts b/src/plugins/vis_type_timelion/server/types.ts
similarity index 100%
rename from src/plugins/timelion/server/types.ts
rename to src/plugins/vis_type_timelion/server/types.ts
diff --git a/src/plugins/vis_type_timeseries/kibana.json b/src/plugins/vis_type_timeseries/kibana.json
index 38662c6a7ff89..9053d2543e0d0 100644
--- a/src/plugins/vis_type_timeseries/kibana.json
+++ b/src/plugins/vis_type_timeseries/kibana.json
@@ -4,6 +4,6 @@
"kibanaVersion": "kibana",
"server": true,
"ui": true,
- "requiredPlugins": ["data", "expressions", "visualizations"],
+ "requiredPlugins": ["charts", "data", "expressions", "visualizations"],
"optionalPlugins": ["usageCollection"]
}
diff --git a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js
index 024f59c3abb1c..d29b795b10ec8 100644
--- a/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js
+++ b/src/plugins/vis_type_timeseries/public/application/components/vis_types/timeseries/config.js
@@ -53,7 +53,7 @@ export const TimeseriesConfig = injectI18n(function(props) {
point_size: '',
value_template: '{{value}}',
offset_time: '',
- split_color_mode: 'gradient',
+ split_color_mode: 'kibana',
axis_min: '',
axis_max: '',
stacked: STACKED_OPTIONS.NONE,
@@ -140,10 +140,10 @@ export const TimeseriesConfig = injectI18n(function(props) {
const splitColorOptions = [
{
label: intl.formatMessage({
- id: 'visTypeTimeseries.timeSeries.gradientLabel',
- defaultMessage: 'Gradient',
+ id: 'visTypeTimeseries.timeSeries.defaultPaletteLabel',
+ defaultMessage: 'Default palette',
}),
- value: 'gradient',
+ value: 'kibana',
},
{
label: intl.formatMessage({
@@ -152,6 +152,13 @@ export const TimeseriesConfig = injectI18n(function(props) {
}),
value: 'rainbow',
},
+ {
+ label: intl.formatMessage({
+ id: 'visTypeTimeseries.timeSeries.gradientLabel',
+ defaultMessage: 'Gradient',
+ }),
+ value: 'gradient',
+ },
];
const selectedSplitColorOption = splitColorOptions.find(option => {
return model.split_color_mode === option.value;
diff --git a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js
index b1c3c7ac6b67a..5cf1619150e5c 100644
--- a/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js
+++ b/src/plugins/vis_type_timeseries/public/application/visualizations/views/timeseries/index.js
@@ -33,7 +33,7 @@ import {
import { EuiIcon } from '@elastic/eui';
import { getTimezone } from '../../../lib/get_timezone';
import { eventBus, ACTIVE_CURSOR } from '../../lib/active_cursor';
-import { getUISettings } from '../../../../services';
+import { getUISettings, getChartsSetup } from '../../../../services';
import { GRID_LINE_CONFIG, ICON_TYPES_MAP, STACKED_OPTIONS } from '../../constants';
import { AreaSeriesDecorator } from './decorators/area_decorator';
import { BarSeriesDecorator } from './decorators/bar_decorator';
@@ -94,6 +94,12 @@ export const TimeSeries = ({
// apply legend style change if bgColor is configured
const classes = classNames('tvbVisTimeSeries', getChartClasses(backgroundColor));
+ // If the color isn't configured by the user, use the color mapping service
+ // to assign a color from the Kibana palette. Colors will be shared across the
+ // session, including dashboards.
+ const { colors } = getChartsSetup();
+ colors.mappedColors.mapKeys(series.filter(({ color }) => !color).map(({ label }) => label));
+
return (
;
visualizations: VisualizationsSetup;
+ charts: ChartsPluginSetup;
}
/** @internal */
@@ -56,10 +59,11 @@ export class MetricsPlugin implements Plugin, void> {
public async setup(
core: CoreSetup,
- { expressions, visualizations }: MetricsPluginSetupDependencies
+ { expressions, visualizations, charts }: MetricsPluginSetupDependencies
) {
expressions.registerFunction(createMetricsFn);
setUISettings(core.uiSettings);
+ setChartsSetup(charts);
visualizations.createReactVisualization(metricsVisDefinition);
}
diff --git a/src/plugins/vis_type_timeseries/public/services.ts b/src/plugins/vis_type_timeseries/public/services.ts
index d93a376584eac..9aa84478fb78b 100644
--- a/src/plugins/vis_type_timeseries/public/services.ts
+++ b/src/plugins/vis_type_timeseries/public/services.ts
@@ -19,6 +19,7 @@
import { I18nStart, SavedObjectsStart, IUiSettingsClient, CoreStart } from 'src/core/public';
import { createGetterSetter } from '../../kibana_utils/public';
+import { ChartsPluginSetup } from '../../charts/public';
import { DataPublicPluginStart } from '../../data/public';
export const [getUISettings, setUISettings] = createGetterSetter('UISettings');
@@ -36,3 +37,7 @@ export const [getCoreStart, setCoreStart] = createGetterSetter('CoreS
export const [getDataStart, setDataStart] = createGetterSetter('DataStart');
export const [getI18n, setI18n] = createGetterSetter('I18n');
+
+export const [getChartsSetup, setChartsSetup] = createGetterSetter(
+ 'ChartsPluginSetup'
+);
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js
index ff8d9077b0871..cad8c8f2025a1 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_split_colors.js
@@ -19,7 +19,7 @@
import Color from 'color';
-export function getSplitColors(inputColor, size = 10, style = 'gradient') {
+export function getSplitColors(inputColor, size = 10, style = 'kibana') {
const color = new Color(inputColor);
const colors = [];
let workingColor = Color.hsl(color.hsl().object());
@@ -49,7 +49,7 @@ export function getSplitColors(inputColor, size = 10, style = 'gradient') {
'#0F1419',
'#666666',
];
- } else {
+ } else if (style === 'gradient') {
colors.push(color.string());
const rotateBy = color.luminosity() / (size - 1);
for (let i = 0; i < size - 1; i++) {
diff --git a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js
index 0874d944033f5..376d32d0da13f 100644
--- a/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js
+++ b/src/plugins/vis_type_timeseries/server/lib/vis_data/helpers/get_splits.test.js
@@ -106,7 +106,7 @@ describe('getSplits(resp, panel, series)', () => {
]);
});
- test('should return a splits for terms group bys', () => {
+ describe('terms group bys', () => {
const resp = {
aggregations: {
SERIES: {
@@ -126,38 +126,89 @@ describe('getSplits(resp, panel, series)', () => {
},
},
};
- const series = {
- id: 'SERIES',
- color: '#F00',
- split_mode: 'terms',
- terms_field: 'beat.hostname',
- terms_size: 10,
- metrics: [
- { id: 'AVG', type: 'avg', field: 'cpu' },
- { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' },
- ],
- };
- const panel = { type: 'timeseries' };
- expect(getSplits(resp, panel, series)).toEqual([
- {
- id: 'SERIES:example-01',
- key: 'example-01',
- label: 'example-01',
- meta: { bucketSize: 10 },
- color: 'rgb(255, 0, 0)',
- timeseries: { buckets: [] },
- SIBAGG: { value: 1 },
- },
- {
- id: 'SERIES:example-02',
- key: 'example-02',
- label: 'example-02',
- meta: { bucketSize: 10 },
- color: 'rgb(147, 0, 0)',
- timeseries: { buckets: [] },
- SIBAGG: { value: 2 },
- },
- ]);
+
+ test('should return a splits with no color', () => {
+ const series = {
+ id: 'SERIES',
+ color: '#F00',
+ split_mode: 'terms',
+ terms_field: 'beat.hostname',
+ terms_size: 10,
+ metrics: [
+ { id: 'AVG', type: 'avg', field: 'cpu' },
+ { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' },
+ ],
+ };
+ const panel = { type: 'timeseries' };
+ expect(getSplits(resp, panel, series)).toEqual([
+ {
+ id: 'SERIES:example-01',
+ key: 'example-01',
+ label: 'example-01',
+ meta: { bucketSize: 10 },
+ color: undefined,
+ timeseries: { buckets: [] },
+ SIBAGG: { value: 1 },
+ },
+ {
+ id: 'SERIES:example-02',
+ key: 'example-02',
+ label: 'example-02',
+ meta: { bucketSize: 10 },
+ color: undefined,
+ timeseries: { buckets: [] },
+ SIBAGG: { value: 2 },
+ },
+ ]);
+ });
+
+ test('should return gradient color', () => {
+ const series = {
+ id: 'SERIES',
+ color: '#F00',
+ split_mode: 'terms',
+ split_color_mode: 'gradient',
+ terms_field: 'beat.hostname',
+ terms_size: 10,
+ metrics: [
+ { id: 'AVG', type: 'avg', field: 'cpu' },
+ { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' },
+ ],
+ };
+ const panel = { type: 'timeseries' };
+ expect(getSplits(resp, panel, series)).toEqual([
+ expect.objectContaining({
+ color: 'rgb(255, 0, 0)',
+ }),
+ expect.objectContaining({
+ color: 'rgb(147, 0, 0)',
+ }),
+ ]);
+ });
+
+ test('should return rainbow color', () => {
+ const series = {
+ id: 'SERIES',
+ color: '#F00',
+ split_mode: 'terms',
+ split_color_mode: 'rainbow',
+ terms_field: 'beat.hostname',
+ terms_size: 10,
+ metrics: [
+ { id: 'AVG', type: 'avg', field: 'cpu' },
+ { id: 'SIBAGG', type: 'avg_bucket', field: 'AVG' },
+ ],
+ };
+ const panel = { type: 'timeseries' };
+ expect(getSplits(resp, panel, series)).toEqual([
+ expect.objectContaining({
+ color: '#68BC00',
+ }),
+ expect.objectContaining({
+ color: '#009CE0',
+ }),
+ ]);
+ });
});
test('should return a splits for filters group bys', () => {
diff --git a/src/plugins/vis_type_timeseries/server/saved_objects/index.ts b/src/plugins/vis_type_timeseries/server/saved_objects/index.ts
new file mode 100644
index 0000000000000..5f7f5767f423d
--- /dev/null
+++ b/src/plugins/vis_type_timeseries/server/saved_objects/index.ts
@@ -0,0 +1,20 @@
+/*
+ * 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 { tsvbTelemetrySavedObjectType } from './tsvb_telemetry';
diff --git a/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts b/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts
similarity index 97%
rename from src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts
rename to src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts
index 77b49e824334f..f18fa1e4cc2fa 100644
--- a/src/plugins/vis_type_timeseries/server/validation_telemetry/saved_object_type.ts
+++ b/src/plugins/vis_type_timeseries/server/saved_objects/tsvb_telemetry.ts
@@ -31,7 +31,7 @@ const resetCount: SavedObjectMigrationFn = doc => ({
export const tsvbTelemetrySavedObjectType: SavedObjectsType = {
name: 'tsvb-validation-telemetry',
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'agnostic',
mappings: {
properties: {
failedRequests: {
diff --git a/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts b/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts
index 779d9441df2fd..e4b8ca19094e4 100644
--- a/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts
+++ b/src/plugins/vis_type_timeseries/server/validation_telemetry/validation_telemetry_service.ts
@@ -19,7 +19,7 @@
import { APICaller, CoreSetup, Plugin, PluginInitializerContext } from 'kibana/server';
import { UsageCollectionSetup } from '../../../usage_collection/server';
-import { tsvbTelemetrySavedObjectType } from './saved_object_type';
+import { tsvbTelemetrySavedObjectType } from '../saved_objects';
export interface ValidationTelemetryServiceSetup {
logFailedValidation: () => void;
diff --git a/src/plugins/visualizations/kibana.json b/src/plugins/visualizations/kibana.json
index cd22b1375ae1b..f3f9cbd8341ec 100644
--- a/src/plugins/visualizations/kibana.json
+++ b/src/plugins/visualizations/kibana.json
@@ -3,5 +3,5 @@
"version": "kibana",
"server": true,
"ui": true,
- "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection"]
+ "requiredPlugins": ["data", "expressions", "uiActions", "embeddable", "usageCollection", "inspector"]
}
diff --git a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
index bf2d174f594b2..8e51bd4ac5d4f 100644
--- a/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
+++ b/src/plugins/visualizations/public/embeddable/create_vis_embeddable_from_object.ts
@@ -28,8 +28,9 @@ import {
getTimeFilter,
getCapabilities,
} from '../services';
+import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
-export const createVisEmbeddableFromObject = async (
+export const createVisEmbeddableFromObject = (deps: VisualizeEmbeddableFactoryDeps) => async (
vis: Vis,
input: Partial & { id: string },
parent?: IContainer
@@ -58,6 +59,7 @@ export const createVisEmbeddableFromObject = async (
indexPatterns,
editUrl,
editable,
+ deps,
},
input,
parent
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
index e64d200251797..ffb028ff131b3 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable.ts
@@ -42,6 +42,7 @@ import { buildPipeline } from '../legacy/build_pipeline';
import { Vis } from '../vis';
import { getExpressions, getUiActions } from '../services';
import { VIS_EVENT_TO_TRIGGER } from './events';
+import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
const getKeys = (o: T): Array => Object.keys(o) as Array;
@@ -50,6 +51,7 @@ export interface VisualizeEmbeddableConfiguration {
indexPatterns?: IIndexPattern[];
editUrl: string;
editable: boolean;
+ deps: VisualizeEmbeddableFactoryDeps;
}
export interface VisualizeInput extends EmbeddableInput {
@@ -84,10 +86,11 @@ export class VisualizeEmbeddable extends Embeddable {
- if (this.handler) {
- return this.handler.openInspector(this.getTitle() || '');
- }
+ if (!this.handler) return;
+
+ const adapters = this.handler.inspect();
+ if (!adapters) return;
+
+ this.deps.start().plugins.inspector.open(adapters, {
+ title: this.getTitle() || '',
+ });
};
/**
diff --git a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
index 4b7d01ae3b246..6ab1c98645988 100644
--- a/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
+++ b/src/plugins/visualizations/public/embeddable/visualize_embeddable_factory.tsx
@@ -25,7 +25,7 @@ import {
EmbeddableOutput,
ErrorEmbeddable,
IContainer,
-} from '../../../../plugins/embeddable/public';
+} from '../../../embeddable/public';
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
@@ -39,11 +39,17 @@ import {
import { showNewVisModal } from '../wizard';
import { convertToSerializedVis } from '../saved_visualizations/_saved_vis';
import { createVisEmbeddableFromObject } from './create_vis_embeddable_from_object';
+import { StartServicesGetter } from '../../../kibana_utils/public';
+import { VisualizationsStartDeps } from '../plugin';
interface VisualizationAttributes extends SavedObjectAttributes {
visState: string;
}
+export interface VisualizeEmbeddableFactoryDeps {
+ start: StartServicesGetter>;
+}
+
export class VisualizeEmbeddableFactory
implements
EmbeddableFactoryDefinition<
@@ -79,7 +85,8 @@ export class VisualizeEmbeddableFactory
return visType.stage !== 'experimental';
},
};
- constructor() {}
+
+ constructor(private readonly deps: VisualizeEmbeddableFactoryDeps) {}
public async isEditable() {
return getCapabilities().visualize.save as boolean;
@@ -101,7 +108,7 @@ export class VisualizeEmbeddableFactory
try {
const savedObject = await savedVisualizations.get(savedObjectId);
const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject));
- return createVisEmbeddableFromObject(vis, input, parent);
+ return createVisEmbeddableFromObject(this.deps)(vis, input, parent);
} catch (e) {
console.error(e); // eslint-disable-line no-console
return new ErrorEmbeddable(e, input, parent);
diff --git a/src/plugins/visualizations/public/mocks.ts b/src/plugins/visualizations/public/mocks.ts
index 2aa346423297a..d6eeffdb01459 100644
--- a/src/plugins/visualizations/public/mocks.ts
+++ b/src/plugins/visualizations/public/mocks.ts
@@ -26,6 +26,7 @@ import { expressionsPluginMock } from '../../../plugins/expressions/public/mocks
import { dataPluginMock } from '../../../plugins/data/public/mocks';
import { usageCollectionPluginMock } from '../../../plugins/usage_collection/public/mocks';
import { uiActionsPluginMock } from '../../../plugins/ui_actions/public/mocks';
+import { inspectorPluginMock } from '../../../plugins/inspector/public/mocks';
const createSetupContract = (): VisualizationsSetup => ({
createBaseVisualization: jest.fn(),
@@ -53,14 +54,16 @@ const createInstance = async () => {
const setup = plugin.setup(coreMock.createSetup(), {
data: dataPluginMock.createSetupContract(),
- expressions: expressionsPluginMock.createSetupContract(),
embeddable: embeddablePluginMock.createSetupContract(),
+ expressions: expressionsPluginMock.createSetupContract(),
+ inspector: inspectorPluginMock.createSetupContract(),
usageCollection: usageCollectionPluginMock.createSetupContract(),
});
const doStart = () =>
plugin.start(coreMock.createStart(), {
data: dataPluginMock.createStartContract(),
expressions: expressionsPluginMock.createStartContract(),
+ inspector: inspectorPluginMock.createStartContract(),
uiActions: uiActionsPluginMock.createStartContract(),
});
diff --git a/src/plugins/visualizations/public/plugin.ts b/src/plugins/visualizations/public/plugin.ts
index 8fcb84b19a9be..b3e8c9b5b61b3 100644
--- a/src/plugins/visualizations/public/plugin.ts
+++ b/src/plugins/visualizations/public/plugin.ts
@@ -43,18 +43,23 @@ import {
VisualizeEmbeddableFactory,
createVisEmbeddableFromObject,
} from './embeddable';
-import { ExpressionsSetup, ExpressionsStart } from '../../../plugins/expressions/public';
-import { EmbeddableSetup } from '../../../plugins/embeddable/public';
+import { ExpressionsSetup, ExpressionsStart } from '../../expressions/public';
+import { EmbeddableSetup } from '../../embeddable/public';
import { visualization as visualizationFunction } from './expressions/visualization_function';
import { visualization as visualizationRenderer } from './expressions/visualization_renderer';
import { range as rangeExpressionFunction } from './expression_functions/range';
import { visDimension as visDimensionExpressionFunction } from './expression_functions/vis_dimension';
import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../plugins/data/public';
-import { UsageCollectionSetup } from '../../../plugins/usage_collection/public';
+import {
+ Setup as InspectorSetup,
+ Start as InspectorStart,
+} from '../../../plugins/inspector/public';
+import { UsageCollectionSetup } from '../../usage_collection/public';
+import { createStartServicesGetter, StartServicesGetter } from '../../kibana_utils/public';
import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations';
import { SerializedVis, Vis } from './vis';
import { showNewVisModal } from './wizard';
-import { UiActionsStart } from '../../../plugins/ui_actions/public';
+import { UiActionsStart } from '../../ui_actions/public';
import {
convertFromSerializedVis,
convertToSerializedVis,
@@ -74,19 +79,21 @@ export interface VisualizationsStart extends TypesStart {
convertToSerializedVis: typeof convertToSerializedVis;
convertFromSerializedVis: typeof convertFromSerializedVis;
showNewVisModal: typeof showNewVisModal;
- __LEGACY: { createVisEmbeddableFromObject: typeof createVisEmbeddableFromObject };
+ __LEGACY: { createVisEmbeddableFromObject: ReturnType };
}
export interface VisualizationsSetupDeps {
- expressions: ExpressionsSetup;
+ data: DataPublicPluginSetup;
embeddable: EmbeddableSetup;
+ expressions: ExpressionsSetup;
+ inspector: InspectorSetup;
usageCollection: UsageCollectionSetup;
- data: DataPublicPluginSetup;
}
export interface VisualizationsStartDeps {
data: DataPublicPluginStart;
expressions: ExpressionsStart;
+ inspector: InspectorStart;
uiActions: UiActionsStart;
}
@@ -107,13 +114,16 @@ export class VisualizationsPlugin
VisualizationsStartDeps
> {
private readonly types: TypesService = new TypesService();
+ private getStartServicesOrDie?: StartServicesGetter;
constructor(initializerContext: PluginInitializerContext) {}
public setup(
- core: CoreSetup,
+ core: CoreSetup,
{ expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps
): VisualizationsSetup {
+ const start = (this.getStartServicesOrDie = createStartServicesGetter(core.getStartServices));
+
setUISettings(core.uiSettings);
setUsageCollector(usageCollection);
@@ -122,7 +132,7 @@ export class VisualizationsPlugin
expressions.registerFunction(rangeExpressionFunction);
expressions.registerFunction(visDimensionExpressionFunction);
- const embeddableFactory = new VisualizeEmbeddableFactory();
+ const embeddableFactory = new VisualizeEmbeddableFactory({ start });
embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
return {
@@ -171,7 +181,11 @@ export class VisualizationsPlugin
convertToSerializedVis,
convertFromSerializedVis,
savedVisualizationsLoader,
- __LEGACY: { createVisEmbeddableFromObject },
+ __LEGACY: {
+ createVisEmbeddableFromObject: createVisEmbeddableFromObject({
+ start: this.getStartServicesOrDie!,
+ }),
+ },
};
}
diff --git a/src/plugins/visualizations/server/saved_objects/visualization.ts b/src/plugins/visualizations/server/saved_objects/visualization.ts
index 9f4782f3ec730..cd2211c185530 100644
--- a/src/plugins/visualizations/server/saved_objects/visualization.ts
+++ b/src/plugins/visualizations/server/saved_objects/visualization.ts
@@ -23,7 +23,7 @@ import { visualizationSavedObjectTypeMigrations } from './visualization_migratio
export const visualizationSavedObjectType: SavedObjectsType = {
name: 'visualization',
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
management: {
icon: 'visualizeApp',
defaultSearchField: 'title',
diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts
index 26f8278cd3d43..83d53d27e41fd 100644
--- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts
+++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.test.ts
@@ -1460,4 +1460,62 @@ describe('migration visualization', () => {
expect(migratedParams.gauge_color_rules[1]).toEqual(params.gauge_color_rules[1]);
});
});
+
+ describe('7.8.0 tsvb split_color_mode', () => {
+ const migrate = (doc: any) =>
+ visualizationSavedObjectTypeMigrations['7.8.0'](
+ doc as Parameters[0],
+ savedObjectMigrationContext
+ );
+
+ const generateDoc = (params: any) => ({
+ attributes: {
+ title: 'My Vis',
+ type: 'visualization',
+ description: 'This is my super cool vis.',
+ visState: JSON.stringify(params),
+ uiStateJSON: '{}',
+ version: 1,
+ kibanaSavedObjectMeta: {
+ searchSourceJSON: '{}',
+ },
+ },
+ });
+
+ it('should change a missing split_color_mode to gradient', () => {
+ const params = { type: 'metrics', params: { series: [{}] } };
+ const testDoc1 = generateDoc(params);
+ const migratedTestDoc1 = migrate(testDoc1);
+ const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series;
+
+ expect(series[0].split_color_mode).toEqual('gradient');
+ });
+
+ it('should not change the color mode if it is set', () => {
+ const params = { type: 'metrics', params: { series: [{ split_color_mode: 'gradient' }] } };
+ const testDoc1 = generateDoc(params);
+ const migratedTestDoc1 = migrate(testDoc1);
+ const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series;
+
+ expect(series[0].split_color_mode).toEqual('gradient');
+ });
+
+ it('should not change the color mode if it is non-default', () => {
+ const params = { type: 'metrics', params: { series: [{ split_color_mode: 'rainbow' }] } };
+ const testDoc1 = generateDoc(params);
+ const migratedTestDoc1 = migrate(testDoc1);
+ const series = JSON.parse(migratedTestDoc1.attributes.visState).params.series;
+
+ expect(series[0].split_color_mode).toEqual('rainbow');
+ });
+
+ it('should not migrate a visualization of unknown type', () => {
+ const params = { type: 'unknown', params: { series: [{}] } };
+ const doc = generateDoc(params);
+ const migratedDoc = migrate(doc);
+ const series = JSON.parse(migratedDoc.attributes.visState).params.series;
+
+ expect(series[0].split_color_mode).toBeUndefined();
+ });
+ });
});
diff --git a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts
index 80783e41863ea..94473e35a942d 100644
--- a/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts
+++ b/src/plugins/visualizations/server/saved_objects/visualization_migrations.ts
@@ -602,7 +602,39 @@ const migrateMatchAllQuery: SavedObjectMigrationFn = doc => {
};
}
}
+ return doc;
+};
+
+// [TSVB] Default color palette is changing, keep the default for older viz
+const migrateTsvbDefaultColorPalettes: SavedObjectMigrationFn = doc => {
+ const visStateJSON = get(doc, 'attributes.visState');
+ let visState;
+
+ if (visStateJSON) {
+ try {
+ visState = JSON.parse(visStateJSON);
+ } catch (e) {
+ // Let it go, the data is invalid and we'll leave it as is
+ }
+ if (visState && visState.type === 'metrics') {
+ const series: any[] = get(visState, 'params.series') || [];
+ series.forEach(part => {
+ // The default value was not saved before
+ if (!part.split_color_mode) {
+ part.split_color_mode = 'gradient';
+ }
+ });
+
+ return {
+ ...doc,
+ attributes: {
+ ...doc.attributes,
+ visState: JSON.stringify(visState),
+ },
+ };
+ }
+ }
return doc;
};
@@ -639,4 +671,5 @@ export const visualizationSavedObjectTypeMigrations = {
'7.3.1': flow(migrateFiltersAggQueryStringQueries),
'7.4.2': flow(transformSplitFiltersStringToQueryObject),
'7.7.0': flow(migrateOperatorKeyTypo),
+ '7.8.0': flow(migrateTsvbDefaultColorPalettes),
};
diff --git a/tasks/config/peg.js b/tasks/config/peg.js
index 1d8667840faba..2de6ff4b5cff9 100644
--- a/tasks/config/peg.js
+++ b/tasks/config/peg.js
@@ -26,7 +26,7 @@ module.exports = {
},
},
timelion_chain: {
- src: 'src/legacy/core_plugins/vis_type_timelion/public/chain.peg',
- dest: 'src/legacy/core_plugins/vis_type_timelion/public/_generated_/chain.js',
+ src: 'src/plugins/vis_type_timelion/public/chain.peg',
+ dest: 'src/plugins/vis_type_timelion/public/_generated_/chain.js',
},
};
diff --git a/test/api_integration/apis/saved_objects/migrations.js b/test/api_integration/apis/saved_objects/migrations.js
index bd207ccb41b20..9d908b95ca575 100644
--- a/test/api_integration/apis/saved_objects/migrations.js
+++ b/test/api_integration/apis/saved_objects/migrations.js
@@ -383,7 +383,7 @@ function migrationsToTypes(migrations) {
return Object.entries(migrations).map(([type, migrations]) => ({
name: type,
hidden: false,
- namespaceAgnostic: false,
+ namespaceType: 'single',
mappings: { properties: {} },
migrations: { ...migrations },
}));
diff --git a/test/examples/config.js b/test/examples/config.js
index 49d75da286075..2be34459d8d06 100644
--- a/test/examples/config.js
+++ b/test/examples/config.js
@@ -28,6 +28,7 @@ export default async function({ readConfigFile }) {
require.resolve('./search'),
require.resolve('./embeddables'),
require.resolve('./ui_actions'),
+ require.resolve('./state_sync'),
],
services: {
...functionalConfig.get('services'),
diff --git a/test/examples/embeddables/adding_children.ts b/test/examples/embeddables/adding_children.ts
index 5fe88b5dd33f0..9ec4b6cffd31a 100644
--- a/test/examples/embeddables/adding_children.ts
+++ b/test/examples/embeddables/adding_children.ts
@@ -25,7 +25,8 @@ export default function({ getService }: PluginFunctionalProviderContext) {
const testSubjects = getService('testSubjects');
const flyout = getService('flyout');
- describe('creating and adding children', () => {
+ // FLAKY: https://github.com/elastic/kibana/issues/58692
+ describe.skip('creating and adding children', () => {
before(async () => {
await testSubjects.click('embeddablePanelExamplae');
});
diff --git a/test/examples/state_sync/index.ts b/test/examples/state_sync/index.ts
new file mode 100644
index 0000000000000..3c524f0feb619
--- /dev/null
+++ b/test/examples/state_sync/index.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
+
+// eslint-disable-next-line import/no-default-export
+export default function({
+ getService,
+ getPageObjects,
+ loadTestFile,
+}: PluginFunctionalProviderContext) {
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['common']);
+
+ describe('state sync examples', function() {
+ before(async () => {
+ await browser.setWindowSize(1300, 900);
+ await PageObjects.common.navigateToApp('settings');
+ });
+
+ loadTestFile(require.resolve('./todo_app'));
+ });
+}
diff --git a/test/examples/state_sync/todo_app.ts b/test/examples/state_sync/todo_app.ts
new file mode 100644
index 0000000000000..4933d746ca4fd
--- /dev/null
+++ b/test/examples/state_sync/todo_app.ts
@@ -0,0 +1,189 @@
+/*
+ * 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 expect from '@kbn/expect';
+
+import { PluginFunctionalProviderContext } from 'test/plugin_functional/services';
+
+// eslint-disable-next-line import/no-default-export
+export default function({ getService, getPageObjects }: PluginFunctionalProviderContext) {
+ const testSubjects = getService('testSubjects');
+ const find = getService('find');
+ const retry = getService('retry');
+ const appsMenu = getService('appsMenu');
+ const browser = getService('browser');
+ const PageObjects = getPageObjects(['common']);
+ const log = getService('log');
+
+ describe('TODO app', () => {
+ describe("TODO app with browser history (platform's ScopedHistory)", async () => {
+ const appId = 'stateContainersExampleBrowserHistory';
+ let base: string;
+
+ before(async () => {
+ base = await PageObjects.common.getHostPort();
+ await appsMenu.clickLink('State containers example - browser history routing');
+ });
+
+ it('links are rendered correctly and state is preserved in links', async () => {
+ const getHrefByLinkTestSubj = async (linkTestSubj: string) =>
+ (await testSubjects.find(linkTestSubj)).getAttribute('href');
+
+ await expectPathname(await getHrefByLinkTestSubj('filterLinkCompleted'), '/completed');
+ await expectPathname(
+ await getHrefByLinkTestSubj('filterLinkNotCompleted'),
+ '/not-completed'
+ );
+ await expectPathname(await getHrefByLinkTestSubj('filterLinkAll'), '/');
+ });
+
+ it('TODO app state is synced with url, back navigation works', async () => {
+ // checking that in initial state checkbox is unchecked and state is synced with url
+ expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false);
+ expect(await browser.getCurrentUrl()).to.contain('completed:!f');
+
+ // check the checkbox by clicking the label (clicking checkbox directly fails as it is "no intractable")
+ (await find.byCssSelector('label[for="0"]')).click();
+
+ // wait for react to update dom and checkbox in checked state
+ await retry.tryForTime(1000, async () => {
+ await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true);
+ });
+ // checking that url is updated with checked state
+ expect(await browser.getCurrentUrl()).to.contain('completed:!t');
+
+ // checking back and forward button
+ await browser.goBack();
+ expect(await browser.getCurrentUrl()).to.contain('completed:!f');
+ await retry.tryForTime(1000, async () => {
+ await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false);
+ });
+
+ await browser.goForward();
+ expect(await browser.getCurrentUrl()).to.contain('completed:!t');
+ await retry.tryForTime(1000, async () => {
+ await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true);
+ });
+ });
+
+ it('links navigation works', async () => {
+ // click link to filter only not completed
+ await testSubjects.click('filterLinkNotCompleted');
+ await expectPathname(await browser.getCurrentUrl(), '/not-completed');
+ // checkbox should be missing because it is "completed"
+ await testSubjects.missingOrFail('todoCheckbox-0');
+ });
+
+ /**
+ * Parses app's scoped pathname from absolute url and asserts it against `expectedPathname`
+ * Also checks that hashes are equal (detail of todo app that state is rendered in links)
+ * @param absoluteUrl
+ * @param expectedPathname
+ */
+ async function expectPathname(absoluteUrl: string, expectedPathname: string) {
+ const scoped = await getScopedUrl(absoluteUrl);
+ const [pathname, newHash] = scoped.split('#');
+ expect(pathname).to.be(expectedPathname);
+ const [, currentHash] = (await browser.getCurrentUrl()).split('#');
+ expect(newHash.replace(/%27/g, "'")).to.be(currentHash.replace(/%27/g, "'"));
+ }
+
+ /**
+ * Get's part of url scoped to this app (removed kibana's host and app's pathname)
+ * @param url - absolute url
+ */
+ async function getScopedUrl(url: string): Promise {
+ expect(url).to.contain(base);
+ expect(url).to.contain(appId);
+ const scopedUrl = url.slice(url.indexOf(appId) + appId.length);
+ expect(scopedUrl).not.to.contain(appId); // app id in url only once
+ return scopedUrl;
+ }
+ });
+
+ describe('TODO app with hash history ', async () => {
+ before(async () => {
+ await appsMenu.clickLink('State containers example - hash history routing');
+ });
+
+ it('Links are rendered correctly and state is preserved in links', async () => {
+ const getHrefByLinkTestSubj = async (linkTestSubj: string) =>
+ (await testSubjects.find(linkTestSubj)).getAttribute('href');
+ await expectHashPathname(await getHrefByLinkTestSubj('filterLinkCompleted'), '/completed');
+ await expectHashPathname(
+ await getHrefByLinkTestSubj('filterLinkNotCompleted'),
+ '/not-completed'
+ );
+ await expectHashPathname(await getHrefByLinkTestSubj('filterLinkAll'), '/');
+ });
+
+ it('TODO app state is synced with url, back navigation works', async () => {
+ // checking that in initial state checkbox is unchecked and state is synced with url
+ expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false);
+ expect(await browser.getCurrentUrl()).to.contain('completed:!f');
+ // check the checkbox by clicking the label (clicking checkbox directly fails as it is "no intractable")
+ (await find.byCssSelector('label[for="0"]')).click();
+
+ // wait for react to update dom and checkbox in checked state
+ await retry.tryForTime(1000, async () => {
+ await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true);
+ });
+ // checking that url is updated with checked state
+ expect(await browser.getCurrentUrl()).to.contain('completed:!t');
+
+ // checking back and forward button
+ await browser.goBack();
+ expect(await browser.getCurrentUrl()).to.contain('completed:!f');
+ await retry.tryForTime(1000, async () => {
+ await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(false);
+ });
+
+ await browser.goForward();
+ expect(await browser.getCurrentUrl()).to.contain('completed:!t');
+ await retry.tryForTime(1000, async () => {
+ await expect(await testSubjects.isChecked('todoCheckbox-0')).to.be(true);
+ });
+ });
+
+ it('links navigation works', async () => {
+ // click link to filter only not completed
+ await testSubjects.click('filterLinkNotCompleted');
+ await expectHashPathname(await browser.getCurrentUrl(), '/not-completed');
+ // checkbox should be missing because it is "completed"
+ await testSubjects.missingOrFail('todoCheckbox-0');
+ });
+
+ /**
+ * Parses app's pathname in hash from absolute url and asserts it against `expectedPathname`
+ * Also checks that queries in hashes are equal (detail of todo app that state is rendered in links)
+ * @param absoluteUrl
+ * @param expectedPathname
+ */
+ async function expectHashPathname(hash: string, expectedPathname: string) {
+ log.debug(`expect hash pathname ${hash} to be ${expectedPathname}`);
+ const hashPath = hash.split('#')[1];
+ const [hashPathname, hashQuery] = hashPath.split('?');
+ const [, currentHash] = (await browser.getCurrentUrl()).split('#');
+ const [, currentHashQuery] = currentHash.split('?');
+ expect(currentHashQuery.replace(/%27/g, "'")).to.be(hashQuery.replace(/%27/g, "'"));
+ expect(hashPathname).to.be(expectedPathname);
+ }
+ });
+ });
+}
diff --git a/test/functional/apps/dashboard/dashboard_back_button.ts b/test/functional/apps/dashboard/dashboard_back_button.ts
new file mode 100644
index 0000000000000..8a488c1780fcc
--- /dev/null
+++ b/test/functional/apps/dashboard/dashboard_back_button.ts
@@ -0,0 +1,47 @@
+/*
+ * 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 expect from '@kbn/expect';
+import { FtrProviderContext } from '../../ftr_provider_context';
+
+export default function({ getService, getPageObjects }: FtrProviderContext) {
+ const esArchiver = getService('esArchiver');
+ const kibanaServer = getService('kibanaServer');
+ const PageObjects = getPageObjects(['dashboard', 'header', 'common', 'visualize', 'timePicker']);
+ const browser = getService('browser');
+
+ describe('dashboard back button', () => {
+ before(async () => {
+ await esArchiver.loadIfNeeded('dashboard/current/kibana');
+ await kibanaServer.uiSettings.replace({
+ defaultIndex: '0bf35f60-3dc9-11e8-8660-4d65aa086b3c',
+ });
+ await PageObjects.common.navigateToApp('dashboard');
+ await PageObjects.dashboard.preserveCrossAppState();
+ });
+
+ it('after navigation from listing page to dashboard back button works', async () => {
+ await PageObjects.dashboard.gotoDashboardLandingPage();
+ await PageObjects.dashboard.loadSavedDashboard('dashboard with everything');
+ await PageObjects.dashboard.waitForRenderComplete();
+ await browser.goBack();
+ expect(await PageObjects.dashboard.onDashboardLandingPage()).to.be(true);
+ });
+ });
+}
diff --git a/test/functional/apps/dashboard/index.js b/test/functional/apps/dashboard/index.js
index 5e96a55b19014..6666ccc57d584 100644
--- a/test/functional/apps/dashboard/index.js
+++ b/test/functional/apps/dashboard/index.js
@@ -55,6 +55,7 @@ export default function({ getService, loadTestFile }) {
loadTestFile(require.resolve('./dashboard_options'));
loadTestFile(require.resolve('./data_shared_attributes'));
loadTestFile(require.resolve('./embed_mode'));
+ loadTestFile(require.resolve('./dashboard_back_button'));
// Note: This one must be last because it unloads some data for one of its tests!
// No, this isn't ideal, but loading/unloading takes so much time and these are all bunched
diff --git a/test/functional/apps/dashboard/panel_controls.js b/test/functional/apps/dashboard/panel_controls.js
index 52c4a11360355..6e24b9f3570a3 100644
--- a/test/functional/apps/dashboard/panel_controls.js
+++ b/test/functional/apps/dashboard/panel_controls.js
@@ -113,6 +113,50 @@ export default function({ getService, getPageObjects }) {
});
});
+ describe('panel cloning', function() {
+ before(async () => {
+ await PageObjects.dashboard.clickNewDashboard();
+ await PageObjects.timePicker.setHistoricalDataRange();
+ await dashboardAddPanel.addVisualization(PIE_CHART_VIS_NAME);
+ });
+
+ after(async function() {
+ await PageObjects.dashboard.gotoDashboardLandingPage();
+ });
+
+ it('clones a panel', async () => {
+ const initialPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ await dashboardPanelActions.clonePanelByTitle(PIE_CHART_VIS_NAME);
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.dashboard.waitForRenderComplete();
+ const postPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ expect(postPanelTitles.length).to.equal(initialPanelTitles.length + 1);
+ });
+
+ it('appends a clone title tag', async () => {
+ const panelTitles = await PageObjects.dashboard.getPanelTitles();
+ expect(panelTitles[1]).to.equal(PIE_CHART_VIS_NAME + ' (copy)');
+ });
+
+ it('retains original panel dimensions', async () => {
+ const panelDimensions = await PageObjects.dashboard.getPanelDimensions();
+ expect(panelDimensions[0]).to.eql(panelDimensions[1]);
+ });
+
+ it('gives a correct title to the clone of a clone', async () => {
+ const initialPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ const clonedPanelName = initialPanelTitles[initialPanelTitles.length - 1];
+ await dashboardPanelActions.clonePanelByTitle(clonedPanelName);
+ await PageObjects.header.waitUntilLoadingHasFinished();
+ await PageObjects.dashboard.waitForRenderComplete();
+ const postPanelTitles = await PageObjects.dashboard.getPanelTitles();
+ expect(postPanelTitles.length).to.equal(initialPanelTitles.length + 1);
+ expect(postPanelTitles[postPanelTitles.length - 1]).to.equal(
+ PIE_CHART_VIS_NAME + ' (copy 1)'
+ );
+ });
+ });
+
describe('panel edit controls', function() {
before(async () => {
await PageObjects.dashboard.clickNewDashboard();
@@ -137,6 +181,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.expectExistsEditPanelAction();
await dashboardPanelActions.expectExistsReplacePanelAction();
+ await dashboardPanelActions.expectExistsDuplicatePanelAction();
await dashboardPanelActions.expectExistsRemovePanelAction();
});
@@ -151,6 +196,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.openContextMenu();
await dashboardPanelActions.expectExistsEditPanelAction();
await dashboardPanelActions.expectExistsReplacePanelAction();
+ await dashboardPanelActions.expectExistsDuplicatePanelAction();
await dashboardPanelActions.expectExistsRemovePanelAction();
// Get rid of the timestamp in the url.
@@ -166,6 +212,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.openContextMenu();
await dashboardPanelActions.expectMissingEditPanelAction();
await dashboardPanelActions.expectMissingReplacePanelAction();
+ await dashboardPanelActions.expectMissingDuplicatePanelAction();
await dashboardPanelActions.expectMissingRemovePanelAction();
});
@@ -174,6 +221,7 @@ export default function({ getService, getPageObjects }) {
await dashboardPanelActions.openContextMenu();
await dashboardPanelActions.expectExistsEditPanelAction();
await dashboardPanelActions.expectExistsReplacePanelAction();
+ await dashboardPanelActions.expectExistsDuplicatePanelAction();
await dashboardPanelActions.expectMissingRemovePanelAction();
await dashboardPanelActions.clickExpandPanelToggle();
});
diff --git a/test/functional/apps/visualize/_tsvb_time_series.ts b/test/functional/apps/visualize/_tsvb_time_series.ts
index fa79190a5bf94..ac89c2b55e514 100644
--- a/test/functional/apps/visualize/_tsvb_time_series.ts
+++ b/test/functional/apps/visualize/_tsvb_time_series.ts
@@ -25,7 +25,6 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
const retry = getService('retry');
const log = getService('log');
const kibanaServer = getService('kibanaServer');
- const testSubjects = getService('testSubjects');
describe('visual builder', function describeIndexTests() {
beforeEach(async () => {
@@ -126,20 +125,18 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
expect(actualCountMin).to.be('3 hours');
});
- // --reversed class is not implemented in @elastic\chart
- describe.skip('Dark mode', () => {
+ describe('Dark mode', () => {
before(async () => {
await kibanaServer.uiSettings.update({
'theme:darkMode': true,
});
});
- it(`viz should have 'reversed' class when background color is white`, async () => {
+ it(`viz should have light class when background color is white`, async () => {
await visualBuilder.clickPanelOptions('timeSeries');
await visualBuilder.setBackgroundColor('#FFFFFF');
- const classNames = await testSubjects.getAttribute('timeseriesChart', 'class');
- expect(classNames.includes('tvbVisTimeSeries--reversed')).to.be(true);
+ expect(await visualBuilder.checkTimeSeriesIsLight()).to.be(true);
});
after(async () => {
diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts
index b8e6c812b46bd..12962b3a5cdef 100644
--- a/test/functional/page_objects/visual_builder_page.ts
+++ b/test/functional/page_objects/visual_builder_page.ts
@@ -71,6 +71,10 @@ export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrPro
}
}
+ public async checkTimeSeriesIsLight() {
+ return await find.existsByCssSelector('.tvbVisTimeSeriesLight');
+ }
+
public async checkTimeSeriesLegendIsPresent() {
const isPresent = await find.existsByCssSelector('.echLegend');
if (!isPresent) {
diff --git a/test/functional/services/dashboard/panel_actions.js b/test/functional/services/dashboard/panel_actions.js
index baea2a52208c1..b155d747f3b93 100644
--- a/test/functional/services/dashboard/panel_actions.js
+++ b/test/functional/services/dashboard/panel_actions.js
@@ -20,6 +20,7 @@
const REMOVE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-deletePanel';
const EDIT_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-editPanel';
const REPLACE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-replacePanel';
+const CLONE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-clonePanel';
const TOGGLE_EXPAND_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-togglePanel';
const CUSTOMIZE_PANEL_DATA_TEST_SUBJ = 'embeddablePanelAction-ACTION_CUSTOMIZE_PANEL';
const OPEN_CONTEXT_MENU_ICON_DATA_TEST_SUBJ = 'embeddablePanelToggleMenuIcon';
@@ -97,6 +98,16 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
await testSubjects.click(REPLACE_PANEL_DATA_TEST_SUBJ);
}
+ async clonePanelByTitle(title) {
+ log.debug(`clonePanel(${title})`);
+ let panelOptions = null;
+ if (title) {
+ panelOptions = await this.getPanelHeading(title);
+ }
+ await this.openContextMenu(panelOptions);
+ await testSubjects.click(CLONE_PANEL_DATA_TEST_SUBJ);
+ }
+
async openInspectorByTitle(title) {
const header = await this.getPanelHeading(title);
await this.openInspector(header);
@@ -123,7 +134,12 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
}
async expectExistsReplacePanelAction() {
- log.debug('expectExistsEditPanelAction');
+ log.debug('expectExistsReplacePanelAction');
+ await testSubjects.existOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
+ }
+
+ async expectExistsDuplicatePanelAction() {
+ log.debug('expectExistsDuplicatePanelAction');
await testSubjects.existOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
}
@@ -133,7 +149,12 @@ export function DashboardPanelActionsProvider({ getService, getPageObjects }) {
}
async expectMissingReplacePanelAction() {
- log.debug('expectMissingEditPanelAction');
+ log.debug('expectMissingReplacePanelAction');
+ await testSubjects.missingOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
+ }
+
+ async expectMissingDuplicatePanelAction() {
+ log.debug('expectMissingDuplicatePanelAction');
await testSubjects.missingOrFail(REPLACE_PANEL_DATA_TEST_SUBJ);
}
diff --git a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/tsconfig.json b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/tsconfig.json
index d8096d9aab27a..544c27241f5cb 100644
--- a/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/tsconfig.json
+++ b/test/plugin_functional/plugins/kbn_tp_embeddable_explorer/tsconfig.json
@@ -6,7 +6,8 @@
"types": [
"node",
"jest",
- "react"
+ "react",
+ "flot"
]
},
"include": [
@@ -16,4 +17,4 @@
"../../../../typings/**/*",
],
"exclude": []
-}
\ No newline at end of file
+}
diff --git a/test/scripts/jenkins_ci_group.sh b/test/scripts/jenkins_ci_group.sh
index 23807a6e98dc2..c77ffe40db553 100755
--- a/test/scripts/jenkins_ci_group.sh
+++ b/test/scripts/jenkins_ci_group.sh
@@ -22,7 +22,7 @@ else
echo " -> running tests from the clone folder"
#yarn run grunt "run:functionalTests_ciGroup${CI_GROUP}";
- node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --config test/functional/config.coverage.js || true;
+ node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
if [[ -d target/kibana-coverage/functional ]]; then
echo " -> replacing kibana${CI_GROUP} with kibana in json files"
diff --git a/test/scripts/jenkins_xpack.sh b/test/scripts/jenkins_xpack.sh
index 5055997df642a..67d88b308ed91 100755
--- a/test/scripts/jenkins_xpack.sh
+++ b/test/scripts/jenkins_xpack.sh
@@ -17,7 +17,7 @@ if [[ -z "$CODE_COVERAGE" ]] ; then
echo " -> Running SIEM cyclic dependency test"
cd "$XPACK_DIR"
- checks-reporter-with-killswitch "X-Pack SIEM cyclic dependency test" node legacy/plugins/siem/scripts/check_circular_deps
+ checks-reporter-with-killswitch "X-Pack SIEM cyclic dependency test" node plugins/siem/scripts/check_circular_deps
echo ""
echo ""
@@ -39,7 +39,7 @@ else
# build runtime for canvas
echo "NODE_ENV=$NODE_ENV"
node ./legacy/plugins/canvas/scripts/shareable_runtime
- node scripts/jest --ci --verbose --coverage
+ node --max-old-space-size=6144 scripts/jest --ci --verbose --coverage
# rename file in order to be unique one
test -f ../target/kibana-coverage/jest/coverage-final.json \
&& mv ../target/kibana-coverage/jest/coverage-final.json \
diff --git a/test/scripts/jenkins_xpack_ci_group.sh b/test/scripts/jenkins_xpack_ci_group.sh
index 01b13293c10ba..a6e600630364e 100755
--- a/test/scripts/jenkins_xpack_ci_group.sh
+++ b/test/scripts/jenkins_xpack_ci_group.sh
@@ -23,7 +23,7 @@ else
cd "kibana${CI_GROUP}/x-pack"
echo " -> running tests from the clone folder"
- node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --config test/functional/config.coverage.js || true;
+ node scripts/functional_tests --debug --include-tag "ciGroup$CI_GROUP" --exclude-tag "skipCoverage" || true;
if [[ -d ../target/kibana-coverage/functional ]]; then
echo " -> replacing kibana${CI_GROUP} with kibana in json files"
diff --git a/test/tsconfig.json b/test/tsconfig.json
index 285d3db64a874..5a3716e620fed 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -3,7 +3,8 @@
"compilerOptions": {
"types": [
"node",
- "mocha"
+ "mocha",
+ "flot"
],
"lib": [
"esnext",
diff --git a/x-pack/.i18nrc.json b/x-pack/.i18nrc.json
index 50f36ddd21c97..c8715ac3447bd 100644
--- a/x-pack/.i18nrc.json
+++ b/x-pack/.i18nrc.json
@@ -32,11 +32,11 @@
"xpack.remoteClusters": "plugins/remote_clusters",
"xpack.painlessLab": "plugins/painless_lab",
"xpack.reporting": ["plugins/reporting", "legacy/plugins/reporting"],
- "xpack.rollupJobs": "legacy/plugins/rollup",
+ "xpack.rollupJobs": ["legacy/plugins/rollup", "plugins/rollup"],
"xpack.searchProfiler": "plugins/searchprofiler",
"xpack.security": ["legacy/plugins/security", "plugins/security"],
"xpack.server": "legacy/server",
- "xpack.siem": "legacy/plugins/siem",
+ "xpack.siem": ["plugins/siem", "legacy/plugins/siem"],
"xpack.snapshotRestore": "plugins/snapshot_restore",
"xpack.spaces": ["legacy/plugins/spaces", "plugins/spaces"],
"xpack.taskManager": "legacy/plugins/task_manager",
diff --git a/x-pack/examples/README.md b/x-pack/examples/README.md
new file mode 100644
index 0000000000000..babf744f9777d
--- /dev/null
+++ b/x-pack/examples/README.md
@@ -0,0 +1,7 @@
+## Example plugins
+
+This folder contains X-Pack example plugins. To run the plugins in this folder, use the `--run-examples` flag, via
+
+```
+yarn start --run-examples
+```
diff --git a/x-pack/examples/ui_actions_enhanced_examples/README.md b/x-pack/examples/ui_actions_enhanced_examples/README.md
new file mode 100644
index 0000000000000..c9f53137d8687
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/README.md
@@ -0,0 +1,3 @@
+## Ui actions enhanced examples
+
+To run this example, use the command `yarn start --run-examples`.
diff --git a/x-pack/examples/ui_actions_enhanced_examples/kibana.json b/x-pack/examples/ui_actions_enhanced_examples/kibana.json
new file mode 100644
index 0000000000000..f75852edced5c
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/kibana.json
@@ -0,0 +1,10 @@
+{
+ "id": "uiActionsEnhancedExamples",
+ "version": "0.0.1",
+ "kibanaVersion": "kibana",
+ "configPath": ["ui_actions_enhanced_examples"],
+ "server": false,
+ "ui": true,
+ "requiredPlugins": ["uiActions", "data"],
+ "optionalPlugins": []
+}
diff --git a/x-pack/examples/ui_actions_enhanced_examples/package.json b/x-pack/examples/ui_actions_enhanced_examples/package.json
new file mode 100644
index 0000000000000..a9f004b075cec
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/package.json
@@ -0,0 +1,17 @@
+{
+ "name": "ui_actions_enhanced_examples",
+ "version": "1.0.0",
+ "main": "target/examples/ui_actions_enhanced_examples",
+ "kibana": {
+ "version": "kibana",
+ "templateVersion": "1.0.0"
+ },
+ "license": "Apache-2.0",
+ "scripts": {
+ "kbn": "node ../../scripts/kbn.js",
+ "build": "rm -rf './target' && tsc"
+ },
+ "devDependencies": {
+ "typescript": "3.7.2"
+ }
+}
diff --git a/x-pack/legacy/plugins/siem/scripts/check_circular_deps.js b/x-pack/examples/ui_actions_enhanced_examples/public/index.ts
similarity index 66%
rename from x-pack/legacy/plugins/siem/scripts/check_circular_deps.js
rename to x-pack/examples/ui_actions_enhanced_examples/public/index.ts
index 046cc010621d7..7f3f36089d576 100644
--- a/x-pack/legacy/plugins/siem/scripts/check_circular_deps.js
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/index.ts
@@ -4,5 +4,6 @@
* you may not use this file except in compliance with the Elastic License.
*/
-require('../../../../../src/setup_node_env');
-require('../dev_tools/circular_deps/run_check_circular_deps_cli');
+import { UiActionsEnhancedExamplesPlugin } from './plugin';
+
+export const plugin = () => new UiActionsEnhancedExamplesPlugin();
diff --git a/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts b/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts
new file mode 100644
index 0000000000000..a4c43753c8247
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/public/plugin.ts
@@ -0,0 +1,31 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { Plugin, CoreSetup, CoreStart } from '../../../../src/core/public';
+import { UiActionsSetup, UiActionsStart } from '../../../../src/plugins/ui_actions/public';
+import { DataPublicPluginSetup, DataPublicPluginStart } from '../../../../src/plugins/data/public';
+
+export interface SetupDependencies {
+ data: DataPublicPluginSetup;
+ uiActions: UiActionsSetup;
+}
+
+export interface StartDependencies {
+ data: DataPublicPluginStart;
+ uiActions: UiActionsStart;
+}
+
+export class UiActionsEnhancedExamplesPlugin
+ implements Plugin {
+ public setup(core: CoreSetup, plugins: SetupDependencies) {
+ // eslint-disable-next-line
+ console.log('ui_actions_enhanced_examples');
+ }
+
+ public start(core: CoreStart, plugins: StartDependencies) {}
+
+ public stop() {}
+}
diff --git a/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json
new file mode 100644
index 0000000000000..d508076b33199
--- /dev/null
+++ b/x-pack/examples/ui_actions_enhanced_examples/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./target",
+ "skipLibCheck": true
+ },
+ "include": [
+ "index.ts",
+ "public/**/*.ts",
+ "public/**/*.tsx",
+ "server/**/*.ts",
+ "../../typings/**/*",
+ ],
+ "exclude": []
+}
diff --git a/x-pack/index.js b/x-pack/index.js
index 61fd4f1752316..1a78c24b1221b 100644
--- a/x-pack/index.js
+++ b/x-pack/index.js
@@ -9,9 +9,7 @@ import { graph } from './legacy/plugins/graph';
import { monitoring } from './legacy/plugins/monitoring';
import { reporting } from './legacy/plugins/reporting';
import { security } from './legacy/plugins/security';
-import { tilemap } from './legacy/plugins/tilemap';
import { dashboardMode } from './legacy/plugins/dashboard_mode';
-import { logstash } from './legacy/plugins/logstash';
import { beats } from './legacy/plugins/beats_management';
import { apm } from './legacy/plugins/apm';
import { maps } from './legacy/plugins/maps';
@@ -40,9 +38,7 @@ module.exports = function(kibana) {
reporting(kibana),
spaces(kibana),
security(kibana),
- tilemap(kibana),
dashboardMode(kibana),
- logstash(kibana),
beats(kibana),
apm(kibana),
maps(kibana),
diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx
index a1462c7637358..cc5c62e25b491 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceDetails/ServiceIntegrations/MachineLearningFlyout/index.tsx
@@ -57,7 +57,8 @@ export class MachineLearningFlyout extends Component {
};
public addErrorToast = () => {
- const core = this.context;
+ const { core } = this.context;
+
const { urlParams } = this.props;
const { serviceName } = urlParams;
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
index 416eb879d5974..d844ac8b5988d 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.test.ts
@@ -166,6 +166,212 @@ describe('waterfall_helpers', () => {
expect(waterfall.errorsCount).toEqual(0);
expect(waterfall).toMatchSnapshot();
});
+ it('should reparent spans', () => {
+ const traceItems = [
+ {
+ processor: { event: 'transaction' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: {
+ duration: { us: 49660 },
+ name: 'GET /api',
+ id: 'myTransactionId1'
+ },
+ timestamp: { us: 1549324795784006 }
+ } as Transaction,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ timestamp: { us: 1549324795825633 },
+ span: {
+ duration: { us: 481 },
+ name: 'SELECT FROM products',
+ id: 'mySpanIdB'
+ },
+ child_ids: ['mySpanIdA', 'mySpanIdC']
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 6161 },
+ name: 'Api::ProductsController#index',
+ id: 'mySpanIdA'
+ },
+ timestamp: { us: 1549324795824504 }
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 532 },
+ name: 'SELECT FROM product',
+ id: 'mySpanIdC'
+ },
+ timestamp: { us: 1549324795827905 }
+ } as Span,
+ {
+ parent: { id: 'myTransactionId1' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 47557 },
+ name: 'GET opbeans-ruby:3000/api/products',
+ id: 'mySpanIdD'
+ },
+ timestamp: { us: 1549324795785760 }
+ } as Span
+ ];
+ const entryTransactionId = 'myTransactionId1';
+ const waterfall = getWaterfall(
+ {
+ trace: { items: traceItems, errorDocs: [], exceedsMax: false },
+ errorsPerTransaction: {}
+ },
+ entryTransactionId
+ );
+ const getIdAndParentId = (item: IWaterfallItem) => ({
+ id: item.id,
+ parentId: item.parent?.id
+ });
+ expect(waterfall.items.length).toBe(5);
+ expect(getIdAndParentId(waterfall.items[0])).toEqual({
+ id: 'myTransactionId1',
+ parentId: undefined
+ });
+ expect(getIdAndParentId(waterfall.items[1])).toEqual({
+ id: 'mySpanIdD',
+ parentId: 'myTransactionId1'
+ });
+ expect(getIdAndParentId(waterfall.items[2])).toEqual({
+ id: 'mySpanIdB',
+ parentId: 'mySpanIdD'
+ });
+ expect(getIdAndParentId(waterfall.items[3])).toEqual({
+ id: 'mySpanIdA',
+ parentId: 'mySpanIdB'
+ });
+ expect(getIdAndParentId(waterfall.items[4])).toEqual({
+ id: 'mySpanIdC',
+ parentId: 'mySpanIdB'
+ });
+ expect(waterfall.errorItems.length).toBe(0);
+ expect(waterfall.errorsCount).toEqual(0);
+ });
+ it("shouldn't reparent spans when child id isn't found", () => {
+ const traceItems = [
+ {
+ processor: { event: 'transaction' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: {
+ duration: { us: 49660 },
+ name: 'GET /api',
+ id: 'myTransactionId1'
+ },
+ timestamp: { us: 1549324795784006 }
+ } as Transaction,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ timestamp: { us: 1549324795825633 },
+ span: {
+ duration: { us: 481 },
+ name: 'SELECT FROM products',
+ id: 'mySpanIdB'
+ },
+ child_ids: ['incorrectId', 'mySpanIdC']
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 6161 },
+ name: 'Api::ProductsController#index',
+ id: 'mySpanIdA'
+ },
+ timestamp: { us: 1549324795824504 }
+ } as Span,
+ {
+ parent: { id: 'mySpanIdD' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-ruby' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 532 },
+ name: 'SELECT FROM product',
+ id: 'mySpanIdC'
+ },
+ timestamp: { us: 1549324795827905 }
+ } as Span,
+ {
+ parent: { id: 'myTransactionId1' },
+ processor: { event: 'span' },
+ trace: { id: 'myTraceId' },
+ service: { name: 'opbeans-node' },
+ transaction: { id: 'myTransactionId1' },
+ span: {
+ duration: { us: 47557 },
+ name: 'GET opbeans-ruby:3000/api/products',
+ id: 'mySpanIdD'
+ },
+ timestamp: { us: 1549324795785760 }
+ } as Span
+ ];
+ const entryTransactionId = 'myTransactionId1';
+ const waterfall = getWaterfall(
+ {
+ trace: { items: traceItems, errorDocs: [], exceedsMax: false },
+ errorsPerTransaction: {}
+ },
+ entryTransactionId
+ );
+ const getIdAndParentId = (item: IWaterfallItem) => ({
+ id: item.id,
+ parentId: item.parent?.id
+ });
+ expect(waterfall.items.length).toBe(5);
+ expect(getIdAndParentId(waterfall.items[0])).toEqual({
+ id: 'myTransactionId1',
+ parentId: undefined
+ });
+ expect(getIdAndParentId(waterfall.items[1])).toEqual({
+ id: 'mySpanIdD',
+ parentId: 'myTransactionId1'
+ });
+ expect(getIdAndParentId(waterfall.items[2])).toEqual({
+ id: 'mySpanIdA',
+ parentId: 'mySpanIdD'
+ });
+ expect(getIdAndParentId(waterfall.items[3])).toEqual({
+ id: 'mySpanIdB',
+ parentId: 'mySpanIdD'
+ });
+ expect(getIdAndParentId(waterfall.items[4])).toEqual({
+ id: 'mySpanIdC',
+ parentId: 'mySpanIdB'
+ });
+ expect(waterfall.errorItems.length).toBe(0);
+ expect(waterfall.errorsCount).toEqual(0);
+ });
});
describe('getWaterfallItems', () => {
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
index 58d9134c4d787..8a873b2ddf1c9 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers.ts
@@ -236,6 +236,29 @@ const getWaterfallItems = (items: TraceAPIResponse['trace']['items']) =>
}
});
+/**
+ * Changes the parent_id of items based on the child_ids property.
+ * Solves the problem of Inferred spans that are created as child of trace spans
+ * when it actually should be its parent.
+ * @param waterfallItems
+ */
+const reparentSpans = (waterfallItems: IWaterfallItem[]) => {
+ return waterfallItems.map(waterfallItem => {
+ if (waterfallItem.docType === 'span') {
+ const { child_ids: childIds } = waterfallItem.doc;
+ if (childIds) {
+ childIds.forEach(childId => {
+ const item = waterfallItems.find(_item => _item.id === childId);
+ if (item) {
+ item.parentId = waterfallItem.id;
+ }
+ });
+ }
+ }
+ return waterfallItem;
+ });
+};
+
const getChildrenGroupedByParentId = (waterfallItems: IWaterfallItem[]) =>
groupBy(waterfallItems, item => (item.parentId ? item.parentId : ROOT_ID));
@@ -306,7 +329,9 @@ export function getWaterfall(
const waterfallItems: IWaterfallItem[] = getWaterfallItems(trace.items);
- const childrenByParentId = getChildrenGroupedByParentId(waterfallItems);
+ const childrenByParentId = getChildrenGroupedByParentId(
+ reparentSpans(waterfallItems)
+ );
const entryWaterfallTransaction = getEntryWaterfallTransaction(
entryTransactionId,
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
index 938962cc9dd18..f681f4dfc675a 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/WaterfallContainer.stories.tsx
@@ -14,7 +14,8 @@ import {
urlParams,
simpleTrace,
traceWithErrors,
- traceChildStartBeforeParent
+ traceChildStartBeforeParent,
+ inferredSpans
} from './waterfallContainer.stories.data';
import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers';
@@ -74,3 +75,22 @@ storiesOf('app/TransactionDetails/Waterfall', module).add(
},
{ info: { source: false } }
);
+
+storiesOf('app/TransactionDetails/Waterfall', module).add(
+ 'inferred spans',
+ () => {
+ const waterfall = getWaterfall(
+ inferredSpans as TraceAPIResponse,
+ 'f2387d37260d00bd'
+ );
+ return (
+
+ );
+ },
+ { info: { source: false } }
+);
diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts
index 835183e73b298..306c8e4f3fedb 100644
--- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts
+++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/waterfallContainer.stories.data.ts
@@ -1645,3 +1645,667 @@ export const traceChildStartBeforeParent = {
},
errorsPerTransaction: {}
};
+
+export const inferredSpans = {
+ trace: {
+ items: [
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ source: {
+ ip: '172.18.0.8'
+ },
+ processor: {
+ name: 'transaction',
+ event: 'transaction'
+ },
+ url: {
+ path: '/api/products/2',
+ scheme: 'http',
+ port: 3000,
+ domain: '172.18.0.7',
+ full: 'http://172.18.0.7:3000/api/products/2'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.786Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ client: {
+ ip: '172.18.0.8'
+ },
+ http: {
+ request: {
+ headers: {
+ Accept: ['*/*'],
+ 'User-Agent': ['Python/3.7 aiohttp/3.3.2'],
+ Host: ['172.18.0.7:3000'],
+ 'Accept-Encoding': ['gzip, deflate']
+ },
+ method: 'get',
+ socket: {
+ encrypted: false,
+ remote_address: '172.18.0.8'
+ }
+ },
+ response: {
+ headers: {
+ 'Transfer-Encoding': ['chunked'],
+ Date: ['Thu, 09 Apr 2020 11:36:01 GMT'],
+ 'Content-Type': ['application/json;charset=UTF-8']
+ },
+ status_code: 200,
+ finished: true,
+ headers_sent: true
+ },
+ version: '1.1'
+ },
+ user_agent: {
+ original: 'Python/3.7 aiohttp/3.3.2',
+ name: 'Other',
+ device: {
+ name: 'Other'
+ }
+ },
+ transaction: {
+ duration: {
+ us: 237537
+ },
+ result: 'HTTP 2xx',
+ name: 'APIRestController#product',
+ span_count: {
+ dropped: 0,
+ started: 3
+ },
+ id: 'f2387d37260d00bd',
+ type: 'request',
+ sampled: true
+ },
+ timestamp: {
+ us: 1586432160786001
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: 'f2387d37260d00bd'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.810Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ span: {
+ duration: {
+ us: 204574
+ },
+ subtype: 'inferred',
+ name: 'ServletInvocableHandlerMethod#invokeAndHandle',
+ id: 'a5df600bd7bd5e38',
+ type: 'app'
+ },
+ timestamp: {
+ us: 1586432160810441
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: 'a5df600bd7bd5e38'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ type: 'apm-server',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.810Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160810441
+ },
+ span: {
+ duration: {
+ us: 102993
+ },
+ stacktrace: [
+ {
+ library_frame: true,
+ exclude_from_grouping: false,
+ filename: 'InvocableHandlerMethod.java',
+ line: {
+ number: -1
+ },
+ function: 'doInvoke'
+ },
+ {
+ exclude_from_grouping: false,
+ library_frame: true,
+ filename: 'InvocableHandlerMethod.java',
+ line: {
+ number: -1
+ },
+ function: 'invokeForRequest'
+ }
+ ],
+ subtype: 'inferred',
+ name: 'APIRestController#product',
+ id: '808dc34fc41ce522',
+ type: 'app'
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: 'f2387d37260d00bd'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ labels: {
+ productId: '2'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.832Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160832300
+ },
+ span: {
+ duration: {
+ us: 99295
+ },
+ name: 'OpenTracing product span',
+ id: '41226ae63af4f235',
+ type: 'unknown'
+ },
+ child_ids: ['8d80de06aa11a6fc']
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: '808dc34fc41ce522'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.859Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160859600
+ },
+ span: {
+ duration: {
+ us: 53835
+ },
+ subtype: 'inferred',
+ name: 'Loader#executeQueryStatement',
+ id: '8d80de06aa11a6fc',
+ type: 'app'
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: '41226ae63af4f235'
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ destination: {
+ address: 'postgres',
+ port: 5432
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.903Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160903236
+ },
+ span: {
+ duration: {
+ us: 10211
+ },
+ subtype: 'postgresql',
+ destination: {
+ service: {
+ resource: 'postgresql',
+ name: 'postgresql',
+ type: 'db'
+ }
+ },
+ name: 'SELECT FROM products',
+ action: 'query',
+ id: '3708d5623658182f',
+ type: 'db',
+ db: {
+ statement:
+ 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?',
+ type: 'sql',
+ user: {
+ name: 'postgres'
+ }
+ }
+ }
+ },
+ {
+ container: {
+ id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ parent: {
+ id: '41226ae63af4f235'
+ },
+ process: {
+ pid: 6,
+ title: '/opt/java/openjdk/bin/java',
+ ppid: 1
+ },
+ agent: {
+ name: 'java',
+ ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3',
+ version: '1.15.1-SNAPSHOT'
+ },
+ destination: {
+ address: 'postgres',
+ port: 5432
+ },
+ processor: {
+ name: 'transaction',
+ event: 'span'
+ },
+ observer: {
+ hostname: '7189f754b5a3',
+ id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc',
+ ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954',
+ type: 'apm-server',
+ version: '8.0.0',
+ version_major: 8
+ },
+ trace: {
+ id: '3b0dc77f3754e5bcb9da0e4c15e0db97'
+ },
+ '@timestamp': '2020-04-09T11:36:00.859Z',
+ ecs: {
+ version: '1.5.0'
+ },
+ service: {
+ node: {
+ name:
+ 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad'
+ },
+ environment: 'production',
+ name: 'opbeans-java',
+ runtime: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ language: {
+ name: 'Java',
+ version: '11.0.6'
+ },
+ version: 'None'
+ },
+ host: {
+ hostname: 'fc2ae281f56f',
+ os: {
+ platform: 'Linux'
+ },
+ ip: '172.18.0.7',
+ name: 'fc2ae281f56f',
+ architecture: 'amd64'
+ },
+ transaction: {
+ id: 'f2387d37260d00bd'
+ },
+ timestamp: {
+ us: 1586432160859508
+ },
+ span: {
+ duration: {
+ us: 4503
+ },
+ subtype: 'postgresql',
+ destination: {
+ service: {
+ resource: 'postgresql',
+ name: 'postgresql',
+ type: 'db'
+ }
+ },
+ name: 'empty query',
+ action: 'query',
+ id: '9871cfd612368932',
+ type: 'db',
+ db: {
+ rows_affected: 0,
+ statement: '(empty query)',
+ type: 'sql',
+ user: {
+ name: 'postgres'
+ }
+ }
+ }
+ }
+ ],
+ exceedsMax: false,
+ errorDocs: []
+ },
+ errorsPerTransaction: {}
+};
diff --git a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts b/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
index 1c618098b36e3..0cd1bdf907531 100644
--- a/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
+++ b/x-pack/legacy/plugins/apm/public/services/rest/ml.ts
@@ -12,7 +12,8 @@ import {
} from '../../../../../../plugins/apm/common/elasticsearch_fieldnames';
import {
getMlJobId,
- getMlPrefix
+ getMlPrefix,
+ encodeForMlApi
} from '../../../../../../plugins/apm/common/ml_job_constants';
import { callApi } from './callApi';
import { ESFilter } from '../../../../../../plugins/apm/typings/elasticsearch';
@@ -53,13 +54,16 @@ export async function startMLJob({
http: HttpSetup;
}) {
const transactionIndices = await getTransactionIndices(http);
- const groups = ['apm', serviceName.toLowerCase()];
+ const groups = [
+ 'apm',
+ encodeForMlApi(serviceName),
+ encodeForMlApi(transactionType)
+ ];
const filter: ESFilter[] = [
{ term: { [SERVICE_NAME]: serviceName } },
{ term: { [PROCESSOR_EVENT]: 'transaction' } },
{ term: { [TRANSACTION_TYPE]: transactionType } }
];
- groups.push(transactionType.toLowerCase());
return callApi(http, {
method: 'POST',
pathname: `/api/ml/modules/setup/apm_transaction`,
diff --git a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss
index 04f2f393d1e80..ae26a1bee99a6 100644
--- a/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss
+++ b/x-pack/legacy/plugins/canvas/canvas_plugin_src/renderers/embeddable/embeddable.scss
@@ -26,8 +26,4 @@
.euiTable {
background: none;
}
-
- .lnsExpressionRenderer {
- @include euiScrollBar;
- }
-}
\ No newline at end of file
+}
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js b/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js
index b50c36aa8df9f..24bc7e17356e2 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js
+++ b/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/services/api.js
@@ -145,9 +145,35 @@ export const updateFollowerIndex = (id, followerIndex) => {
if (isUsingAdvancedSettings) {
uiMetrics.push(UIM_FOLLOWER_INDEX_USE_ADVANCED_OPTIONS);
}
+
+ const {
+ maxReadRequestOperationCount,
+ maxOutstandingReadRequests,
+ maxReadRequestSize,
+ maxWriteRequestOperationCount,
+ maxWriteRequestSize,
+ maxOutstandingWriteRequests,
+ maxWriteBufferCount,
+ maxWriteBufferSize,
+ maxRetryDelay,
+ readPollTimeout,
+ } = followerIndex;
+
const request = httpClient.put(`${API_BASE_PATH}/follower_indices/${encodeURIComponent(id)}`, {
- body: JSON.stringify(followerIndex),
+ body: JSON.stringify({
+ maxReadRequestOperationCount,
+ maxOutstandingReadRequests,
+ maxReadRequestSize,
+ maxWriteRequestOperationCount,
+ maxWriteRequestSize,
+ maxOutstandingWriteRequests,
+ maxWriteBufferCount,
+ maxWriteBufferSize,
+ maxRetryDelay,
+ readPollTimeout,
+ }),
});
+
return trackUserRequest(request, uiMetrics);
};
diff --git a/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts b/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts
index 3896e1c02c915..1d7dacf4a8688 100644
--- a/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts
+++ b/x-pack/legacy/plugins/cross_cluster_replication/server/np_ready/routes/api/follower_index.ts
@@ -164,6 +164,18 @@ export const registerFollowerIndexRoutes = ({ router, __LEGACY }: RouteDependenc
path: `${API_BASE_PATH}/follower_indices/{id}`,
validate: {
params: schema.object({ id: schema.string() }),
+ body: schema.object({
+ maxReadRequestOperationCount: schema.maybe(schema.number()),
+ maxOutstandingReadRequests: schema.maybe(schema.number()),
+ maxReadRequestSize: schema.maybe(schema.string()), // byte value
+ maxWriteRequestOperationCount: schema.maybe(schema.number()),
+ maxWriteRequestSize: schema.maybe(schema.string()), // byte value
+ maxOutstandingWriteRequests: schema.maybe(schema.number()),
+ maxWriteBufferCount: schema.maybe(schema.number()),
+ maxWriteBufferSize: schema.maybe(schema.string()), // byte value
+ maxRetryDelay: schema.maybe(schema.string()), // time value
+ readPollTimeout: schema.maybe(schema.string()), // time value
+ }),
},
},
licensePreRoutingFactory({
diff --git a/x-pack/legacy/plugins/logstash/README.md b/x-pack/legacy/plugins/logstash/README.md
deleted file mode 100755
index 7d181249300fa..0000000000000
--- a/x-pack/legacy/plugins/logstash/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## Logstash Plugin
-
-This plugin adds Logstash specific UI code to x-pack. Currently this plugin adds just the management features.
diff --git a/x-pack/legacy/plugins/logstash/common/lib/__tests__/get_moment.js b/x-pack/legacy/plugins/logstash/common/lib/__tests__/get_moment.js
deleted file mode 100755
index 2e63b231bec32..0000000000000
--- a/x-pack/legacy/plugins/logstash/common/lib/__tests__/get_moment.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import expect from '@kbn/expect';
-import { getMoment } from '../get_moment';
-
-describe('get_moment', () => {
- describe('getMoment', () => {
- it(`returns a moment object when passed a date`, () => {
- const moment = getMoment('2017-03-30T14:53:08.121Z');
-
- expect(moment.constructor.name).to.be('Moment');
- });
-
- it(`returns null when passed falsy`, () => {
- const results = [
- getMoment(false),
- getMoment(0),
- getMoment(''),
- getMoment(null),
- getMoment(undefined),
- getMoment(NaN),
- ];
-
- results.forEach(result => {
- expect(result).to.be(null);
- });
- });
- });
-});
diff --git a/x-pack/legacy/plugins/logstash/index.js b/x-pack/legacy/plugins/logstash/index.js
deleted file mode 100755
index 29f01032f3413..0000000000000
--- a/x-pack/legacy/plugins/logstash/index.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { resolve } from 'path';
-import { registerLicenseChecker } from './server/lib/register_license_checker';
-import { PLUGIN } from '../../../plugins/logstash/common/constants';
-
-export const logstash = kibana =>
- new kibana.Plugin({
- id: PLUGIN.ID,
- publicDir: resolve(__dirname, 'public'),
- require: ['kibana', 'elasticsearch', 'xpack_main'],
- configPrefix: 'xpack.logstash',
- config(Joi) {
- return Joi.object({
- enabled: Joi.boolean().default(true),
- }).default();
- },
- uiExports: {
- managementSections: [
- 'plugins/logstash/sections/pipeline_list',
- 'plugins/logstash/sections/pipeline_edit',
- ],
- home: ['plugins/logstash/lib/register_home_feature'],
- },
- init: server => {
- registerLicenseChecker(server);
- },
- });
diff --git a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts b/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts
deleted file mode 100644
index 2e1ee2afb9ce6..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/lib/register_home_feature.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-import { npSetup } from 'ui/new_platform';
-// @ts-ignore
-import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
-import { FeatureCatalogueCategory } from '../../../../../../src/plugins/home/public';
-// @ts-ignore
-import { PLUGIN } from '../../../../../plugins/logstash/common/constants';
-
-const {
- plugins: { home },
-} = npSetup;
-
-const enableLinks = Boolean(xpackInfo.get(`features.${PLUGIN.ID}.enableLinks`));
-
-if (enableLinks) {
- home.featureCatalogue.register({
- id: 'management_logstash',
- title: i18n.translate('xpack.logstash.homeFeature.logstashPipelinesTitle', {
- defaultMessage: 'Logstash Pipelines',
- }),
- description: i18n.translate('xpack.logstash.homeFeature.logstashPipelinesDescription', {
- defaultMessage: 'Create, delete, update, and clone data ingestion pipelines.',
- }),
- icon: 'pipelineApp',
- path: '/app/kibana#/management/logstash/pipelines',
- showOnHomePage: true,
- category: FeatureCatalogueCategory.ADMIN,
- });
-}
diff --git a/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/update_logstash_sections.js b/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/update_logstash_sections.js
deleted file mode 100755
index 8da687529c846..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/lib/update_management_sections/update_logstash_sections.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { management } from 'ui/management';
-
-export function updateLogstashSections(pipelineId) {
- const editSection = management.getSection('logstash/pipelines/pipeline/edit');
- const newSection = management.getSection('logstash/pipelines/pipeline/new');
-
- newSection.hide();
- editSection.hide();
-
- if (pipelineId) {
- editSection.url = `#/management/logstash/pipelines/pipeline/${pipelineId}/edit`;
- editSection.show();
- } else {
- newSection.show();
- }
-}
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/pipeline_edit.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/pipeline_edit.js
deleted file mode 100755
index 83446278fdeca..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/pipeline_edit/pipeline_edit.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { render } from 'react-dom';
-import { isEmpty } from 'lodash';
-import { uiModules } from 'ui/modules';
-import { npSetup } from 'ui/new_platform';
-import { toastNotifications } from 'ui/notify';
-import { I18nContext } from 'ui/i18n';
-import { PipelineEditor } from '../../../../components/pipeline_editor';
-import 'plugins/logstash/services/license';
-import { logstashSecurity } from 'plugins/logstash/services/security';
-import 'ace';
-
-const app = uiModules.get('xpack/logstash');
-
-app.directive('pipelineEdit', function($injector) {
- const pipelineService = $injector.get('pipelineService');
- const licenseService = $injector.get('logstashLicenseService');
- const kbnUrl = $injector.get('kbnUrl');
- const $route = $injector.get('$route');
-
- return {
- restrict: 'E',
- link: async (scope, el) => {
- const close = () => scope.$evalAsync(kbnUrl.change('/management/logstash/pipelines', {}));
- const open = id =>
- scope.$evalAsync(kbnUrl.change(`/management/logstash/pipelines/${id}/edit`));
-
- const userResource = logstashSecurity.isSecurityEnabled()
- ? await npSetup.plugins.security.authc.getCurrentUser()
- : null;
-
- render(
-
-
- ,
- el[0]
- );
- },
- scope: {
- pipeline: '=',
- },
- };
-});
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/upgrade_failure.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/upgrade_failure.js
deleted file mode 100755
index 2ef99d3b47672..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/components/upgrade_failure/upgrade_failure.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { render } from 'react-dom';
-import { isEmpty } from 'lodash';
-import { uiModules } from 'ui/modules';
-import { I18nContext } from 'ui/i18n';
-import { UpgradeFailure } from '../../../../components/upgrade_failure';
-
-const app = uiModules.get('xpack/logstash');
-
-app.directive('upgradeFailure', $injector => {
- const $route = $injector.get('$route');
- const kbnUrl = $injector.get('kbnUrl');
-
- return {
- link: (scope, el) => {
- const onRetry = () => {
- $route.updateParams({ retry: true });
- $route.reload();
- };
- const onClose = () => {
- scope.$evalAsync(kbnUrl.change('management/logstash/pipelines', {}));
- };
- const isNewPipeline = isEmpty(scope.pipeline.id);
- const isManualUpgrade = !!$route.current.params.retry;
-
- render(
-
-
- ,
- el[0]
- );
- },
- restrict: 'E',
- scope: {
- pipeline: '=',
- },
- };
-});
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.html b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.html
deleted file mode 100755
index e1c422d46dfdb..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.js
deleted file mode 100755
index 733f7dc3ae2e6..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_edit/pipeline_edit_route.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import routes from 'ui/routes';
-import { toastNotifications } from 'ui/notify';
-import { i18n } from '@kbn/i18n';
-import template from './pipeline_edit_route.html';
-import 'plugins/logstash/services/pipeline';
-import 'plugins/logstash/services/license';
-import 'plugins/logstash/services/upgrade';
-import './components/pipeline_edit';
-import './components/upgrade_failure';
-import { updateLogstashSections } from 'plugins/logstash/lib/update_management_sections';
-import { Pipeline } from 'plugins/logstash/models/pipeline';
-import { getPipelineCreateBreadcrumbs, getPipelineEditBreadcrumbs } from '../breadcrumbs';
-
-routes
- .when('/management/logstash/pipelines/pipeline/:id/edit', {
- k7Breadcrumbs: getPipelineEditBreadcrumbs,
- })
- .when('/management/logstash/pipelines/new-pipeline', {
- k7Breadcrumbs: getPipelineCreateBreadcrumbs,
- })
- .defaults(/management\/logstash\/pipelines\/(new-pipeline|pipeline\/:id\/edit)/, {
- template: template,
- controller: class PipelineEditRouteController {
- constructor($injector) {
- const $route = $injector.get('$route');
- this.pipeline = $route.current.locals.pipeline;
- this.isUpgraded = $route.current.locals.isUpgraded;
- }
- },
- controllerAs: 'pipelineEditRoute',
- resolve: {
- logstashTabs: $injector => {
- const $route = $injector.get('$route');
- const pipelineId = $route.current.params.id;
- updateLogstashSections(pipelineId);
- },
- pipeline: function($injector) {
- const $route = $injector.get('$route');
- const pipelineService = $injector.get('pipelineService');
- const licenseService = $injector.get('logstashLicenseService');
- const kbnUrl = $injector.get('kbnUrl');
-
- const pipelineId = $route.current.params.id;
-
- if (!pipelineId) return new Pipeline();
-
- return pipelineService
- .loadPipeline(pipelineId)
- .then(pipeline => (!!$route.current.params.clone ? pipeline.clone : pipeline))
- .catch(err => {
- return licenseService.checkValidity().then(() => {
- if (err.status !== 403) {
- toastNotifications.addDanger(
- i18n.translate('xpack.logstash.couldNotLoadPipelineErrorNotification', {
- defaultMessage: `Couldn't load pipeline. Error: '{errStatusText}'.`,
- values: {
- errStatusText: err.statusText,
- },
- })
- );
- }
-
- kbnUrl.redirect('/management/logstash/pipelines');
- return Promise.reject();
- });
- });
- },
- checkLicense: $injector => {
- const licenseService = $injector.get('logstashLicenseService');
- return licenseService.checkValidity();
- },
- isUpgraded: $injector => {
- const upgradeService = $injector.get('upgradeService');
- return upgradeService.executeUpgrade();
- },
- },
- });
-
-routes.when('/management/logstash/pipelines/pipeline/:id', {
- redirectTo: '/management/logstash/pipelines/pipeline/:id/edit',
-});
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/index.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/index.js
deleted file mode 100755
index 7e8ca0e4c2c57..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './pipeline_list';
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/pipeline_list.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/pipeline_list.js
deleted file mode 100755
index b856979aed8b6..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/components/pipeline_list/pipeline_list.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { render } from 'react-dom';
-import { uiModules } from 'ui/modules';
-import { toastNotifications } from 'ui/notify';
-import { I18nContext } from 'ui/i18n';
-import { PipelineList } from '../../../../components/pipeline_list';
-import 'plugins/logstash/services/pipelines';
-import 'plugins/logstash/services/license';
-import 'plugins/logstash/services/cluster';
-import 'plugins/logstash/services/monitoring';
-
-const app = uiModules.get('xpack/logstash');
-
-app.directive('pipelineList', function($injector) {
- const pipelinesService = $injector.get('pipelinesService');
- const licenseService = $injector.get('logstashLicenseService');
- const clusterService = $injector.get('xpackLogstashClusterService');
- const monitoringService = $injector.get('xpackLogstashMonitoringService');
- const kbnUrl = $injector.get('kbnUrl');
-
- return {
- restrict: 'E',
- link: (scope, el) => {
- const openPipeline = id =>
- scope.$evalAsync(kbnUrl.change(`management/logstash/pipelines/pipeline/${id}/edit`));
- const createPipeline = () =>
- scope.$evalAsync(kbnUrl.change('management/logstash/pipelines/new-pipeline'));
- const clonePipeline = id =>
- scope.$evalAsync(kbnUrl.change(`management/logstash/pipelines/pipeline/${id}/edit?clone`));
- render(
-
-
- ,
- el[0]
- );
- },
- scope: {},
- controllerAs: 'pipelineList',
- };
-});
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/index.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/index.js
deleted file mode 100755
index f60decd1378d5..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/index.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './register_management_section';
-import './pipeline_list_route';
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.html b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.html
deleted file mode 100755
index 55b3fabd70161..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.js
deleted file mode 100755
index eb593207572c2..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/pipeline_list_route.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import routes from 'ui/routes';
-import { management } from 'ui/management';
-import template from './pipeline_list_route.html';
-import './components/pipeline_list';
-import 'plugins/logstash/services/license';
-import { getPipelineListBreadcrumbs } from '../breadcrumbs';
-
-routes.when('/management/logstash/pipelines/', {
- template,
- k7Breadcrumbs: getPipelineListBreadcrumbs,
-});
-
-routes.defaults(/\/management/, {
- resolve: {
- logstashManagementSection: $injector => {
- const licenseService = $injector.get('logstashLicenseService');
- const logstashSection = management.getSection('logstash/pipelines');
-
- if (licenseService.enableLinks) {
- logstashSection.show();
- logstashSection.enable();
- } else {
- logstashSection.hide();
- }
- },
- },
-});
diff --git a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/register_management_section.js b/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/register_management_section.js
deleted file mode 100755
index e285418f5f2ae..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/sections/pipeline_list/register_management_section.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { management } from 'ui/management';
-import { i18n } from '@kbn/i18n';
-
-management.getSection('logstash').register('pipelines', {
- display: i18n.translate('xpack.logstash.managementSection.pipelinesTitle', {
- defaultMessage: 'Pipelines',
- }),
- order: 10,
- url: '#/management/logstash/pipelines/',
-});
-
-management.getSection('logstash/pipelines').register('pipeline', {
- visible: false,
-});
-
-management.getSection('logstash/pipelines/pipeline').register('edit', {
- display: i18n.translate('xpack.logstash.managementSection.editPipelineTitle', {
- defaultMessage: 'Edit pipeline',
- }),
- order: 1,
- visible: false,
-});
-
-management.getSection('logstash/pipelines/pipeline').register('new', {
- display: i18n.translate('xpack.logstash.managementSection.createPipelineTitle', {
- defaultMessage: 'Create pipeline',
- }),
- order: 1,
- visible: false,
-});
diff --git a/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.factory.js
deleted file mode 100755
index 0fee2804c704d..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/cluster/cluster_service.factory.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import { ClusterService } from './cluster_service';
-
-uiModules.get('xpack/logstash').factory('xpackLogstashClusterService', $injector => {
- const $http = $injector.get('$http');
- return new ClusterService($http);
-});
diff --git a/x-pack/legacy/plugins/logstash/public/services/cluster/index.js b/x-pack/legacy/plugins/logstash/public/services/cluster/index.js
deleted file mode 100755
index ba52657a27ca8..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/cluster/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './cluster_service.factory';
diff --git a/x-pack/legacy/plugins/logstash/public/services/license/index.js b/x-pack/legacy/plugins/logstash/public/services/license/index.js
deleted file mode 100755
index 8be8fb5ccbc64..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/license/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './license_service.factory';
diff --git a/x-pack/legacy/plugins/logstash/public/services/license/license_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/license/license_service.factory.js
deleted file mode 100755
index 0e131f9b94008..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/license/license_service.factory.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
-import 'ui/url';
-import { LogstashLicenseService } from './logstash_license_service';
-
-uiModules.get('xpack/logstash').factory('logstashLicenseService', ($timeout, kbnUrl) => {
- return new LogstashLicenseService(xpackInfo, kbnUrl, $timeout);
-});
diff --git a/x-pack/legacy/plugins/logstash/public/services/license/logstash_license_service.js b/x-pack/legacy/plugins/logstash/public/services/license/logstash_license_service.js
deleted file mode 100755
index 69cc8614a6ae2..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/license/logstash_license_service.js
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import React from 'react';
-import { toastNotifications } from 'ui/notify';
-import { MarkdownSimple } from '../../../../../../../src/plugins/kibana_react/public';
-import { PLUGIN } from '../../../../../../plugins/logstash/common/constants';
-
-export class LogstashLicenseService {
- constructor(xpackInfoService, kbnUrlService, $timeout) {
- this.xpackInfoService = xpackInfoService;
- this.kbnUrlService = kbnUrlService;
- this.$timeout = $timeout;
- }
-
- get enableLinks() {
- return Boolean(this.xpackInfoService.get(`features.${PLUGIN.ID}.enableLinks`));
- }
-
- get isAvailable() {
- return Boolean(this.xpackInfoService.get(`features.${PLUGIN.ID}.isAvailable`));
- }
-
- get isReadOnly() {
- return Boolean(this.xpackInfoService.get(`features.${PLUGIN.ID}.isReadOnly`));
- }
-
- get message() {
- return this.xpackInfoService.get(`features.${PLUGIN.ID}.message`);
- }
-
- notifyAndRedirect() {
- toastNotifications.addDanger({
- title: (
-
- {this.xpackInfoService.get(`features.${PLUGIN.ID}.message`)}
-
- ),
- });
- this.kbnUrlService.redirect('/management');
- }
-
- /**
- * Checks if the license is valid or the license can perform downgraded UI tasks.
- * Otherwise, notifies and redirects.
- */
- checkValidity() {
- return new Promise((resolve, reject) => {
- this.$timeout(() => {
- if (this.isAvailable) {
- return resolve();
- }
-
- this.notifyAndRedirect();
- return reject();
- }, 10); // To allow latest XHR call to update license info
- });
- }
-}
diff --git a/x-pack/legacy/plugins/logstash/public/services/monitoring/index.js b/x-pack/legacy/plugins/logstash/public/services/monitoring/index.js
deleted file mode 100755
index 83b2105beb5ef..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/monitoring/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './monitoring_service.factory';
diff --git a/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.factory.js
deleted file mode 100755
index 271c776dd6f69..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/monitoring/monitoring_service.factory.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import { MonitoringService } from './monitoring_service';
-import '../cluster';
-
-uiModules.get('xpack/logstash').factory('xpackLogstashMonitoringService', $injector => {
- const $http = $injector.get('$http');
- const Promise = $injector.get('Promise');
- const monitoringUiEnabled =
- $injector.has('monitoringUiEnabled') && $injector.get('monitoringUiEnabled');
- const clusterService = $injector.get('xpackLogstashClusterService');
- return new MonitoringService($http, Promise, monitoringUiEnabled, clusterService);
-});
diff --git a/x-pack/legacy/plugins/logstash/public/services/pipeline/index.js b/x-pack/legacy/plugins/logstash/public/services/pipeline/index.js
deleted file mode 100755
index 3b0e28bd555e6..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/pipeline/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './pipeline_service.factory';
diff --git a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.factory.js
deleted file mode 100755
index cf93915425213..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.factory.js
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import { PipelineService } from './pipeline_service';
-
-uiModules.get('xpack/logstash').factory('pipelineService', $injector => {
- const $http = $injector.get('$http');
- const pipelinesService = $injector.get('pipelinesService');
- return new PipelineService($http, pipelinesService);
-});
diff --git a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.js b/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.js
deleted file mode 100755
index b5d0dbeb852d5..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/pipeline/pipeline_service.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import chrome from 'ui/chrome';
-import { ROUTES } from '../../../../../../plugins/logstash/common/constants';
-import { Pipeline } from 'plugins/logstash/models/pipeline';
-
-export class PipelineService {
- constructor($http, pipelinesService) {
- this.$http = $http;
- this.pipelinesService = pipelinesService;
- this.basePath = chrome.addBasePath(ROUTES.API_ROOT);
- }
-
- loadPipeline(id) {
- return this.$http.get(`${this.basePath}/pipeline/${id}`).then(response => {
- return Pipeline.fromUpstreamJSON(response.data);
- });
- }
-
- savePipeline(pipelineModel) {
- return this.$http
- .put(`${this.basePath}/pipeline/${pipelineModel.id}`, pipelineModel.upstreamJSON)
- .catch(e => {
- throw e.data.message;
- });
- }
-
- deletePipeline(id) {
- return this.$http
- .delete(`${this.basePath}/pipeline/${id}`)
- .then(() => this.pipelinesService.addToRecentlyDeleted(id))
- .catch(e => {
- throw e.data.message;
- });
- }
-}
diff --git a/x-pack/legacy/plugins/logstash/public/services/pipelines/index.js b/x-pack/legacy/plugins/logstash/public/services/pipelines/index.js
deleted file mode 100755
index e273e12d46c6d..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/pipelines/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './pipelines_service.factory';
diff --git a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.factory.js
deleted file mode 100755
index 9295949e001eb..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.factory.js
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import { PipelinesService } from './pipelines_service';
-import '../monitoring';
-
-uiModules.get('xpack/logstash').factory('pipelinesService', $injector => {
- const $http = $injector.get('$http');
- const $window = $injector.get('$window');
- const Promise = $injector.get('Promise');
- const monitoringService = $injector.get('xpackLogstashMonitoringService');
- return new PipelinesService($http, $window, Promise, monitoringService);
-});
diff --git a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.js b/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.js
deleted file mode 100755
index d70c8be06fde4..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/pipelines/pipelines_service.js
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import chrome from 'ui/chrome';
-import { ROUTES, MONITORING } from '../../../../../../plugins/logstash/common/constants';
-import { PipelineListItem } from 'plugins/logstash/models/pipeline_list_item';
-
-const RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY = 'xpack.logstash.recentlyDeletedPipelines';
-
-export class PipelinesService {
- constructor($http, $window, Promise, monitoringService) {
- this.$http = $http;
- this.$window = $window;
- this.Promise = Promise;
- this.monitoringService = monitoringService;
- this.basePath = chrome.addBasePath(ROUTES.API_ROOT);
- }
-
- getPipelineList() {
- return this.Promise.all([
- this.getManagementPipelineList(),
- this.getMonitoringPipelineList(),
- ]).then(([managementPipelines, monitoringPipelines]) => {
- const now = Date.now();
-
- // Monitoring will report centrally-managed pipelines as well, including recently-deleted centrally-managed ones.
- // If there's a recently-deleted pipeline we're keeping track of BUT monitoring doesn't report it, that means
- // it's not running in Logstash any more. So we can stop tracking it as a recently-deleted pipeline.
- const monitoringPipelineIds = monitoringPipelines.map(pipeline => pipeline.id);
- this.getRecentlyDeleted().forEach(recentlyDeletedPipeline => {
- // We don't want to stop tracking the recently-deleted pipeline until Monitoring has had some
- // time to report on it. Otherwise, if we stop tracking first, *then* Monitoring reports it, we'll
- // still end up showing it in the list until Monitoring stops reporting it.
- if (now - recentlyDeletedPipeline.deletedOn < MONITORING.ACTIVE_PIPELINE_RANGE_S * 1000) {
- return;
- }
-
- // If Monitoring is still reporting the pipeline, don't stop tracking it yet
- if (monitoringPipelineIds.includes(recentlyDeletedPipeline.id)) {
- return;
- }
-
- this.removeFromRecentlyDeleted(recentlyDeletedPipeline.id);
- });
-
- // Merge centrally-managed pipelines with pipelines reported by monitoring. Take care to dedupe
- // while merging because monitoring will (rightly) report centrally-managed pipelines as well,
- // including recently-deleted ones!
- const managementPipelineIds = managementPipelines.map(pipeline => pipeline.id);
- return managementPipelines.concat(
- monitoringPipelines.filter(
- monitoringPipeline =>
- !managementPipelineIds.includes(monitoringPipeline.id) &&
- !this.isRecentlyDeleted(monitoringPipeline.id)
- )
- );
- });
- }
-
- getManagementPipelineList() {
- return this.$http
- .get(`${this.basePath}/pipelines`)
- .then(response =>
- response.data.pipelines.map(pipeline => PipelineListItem.fromUpstreamJSON(pipeline))
- );
- }
-
- getMonitoringPipelineList() {
- return this.monitoringService.getPipelineList();
- }
-
- /**
- * Delete a collection of pipelines
- *
- * @param pipelineIds Array of pipeline IDs
- * @return Promise { numSuccesses, numErrors }
- */
- deletePipelines(pipelineIds) {
- const body = {
- pipelineIds,
- };
- return this.$http.post(`${this.basePath}/pipelines/delete`, body).then(response => {
- this.addToRecentlyDeleted(...pipelineIds);
- return response.data.results;
- });
- }
-
- addToRecentlyDeleted(...pipelineIds) {
- const recentlyDeletedPipelines = this.getRecentlyDeleted();
- const recentlyDeletedPipelineIds = recentlyDeletedPipelines.map(pipeline => pipeline.id);
- pipelineIds.forEach(pipelineId => {
- if (!recentlyDeletedPipelineIds.includes(pipelineId)) {
- recentlyDeletedPipelines.push({
- id: pipelineId,
- deletedOn: Date.now(),
- });
- }
- });
- this.setRecentlyDeleted(recentlyDeletedPipelines);
- }
-
- removeFromRecentlyDeleted(...pipelineIds) {
- const recentlyDeletedPipelinesToKeep = this.getRecentlyDeleted().filter(
- recentlyDeletedPipeline => !pipelineIds.includes(recentlyDeletedPipeline.id)
- );
- this.setRecentlyDeleted(recentlyDeletedPipelinesToKeep);
- }
-
- isRecentlyDeleted(pipelineId) {
- return this.getRecentlyDeleted()
- .map(pipeline => pipeline.id)
- .includes(pipelineId);
- }
-
- getRecentlyDeleted() {
- const recentlyDeletedPipelines = this.$window.localStorage.getItem(
- RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY
- );
- if (!recentlyDeletedPipelines) {
- return [];
- }
-
- return JSON.parse(recentlyDeletedPipelines);
- }
-
- setRecentlyDeleted(recentlyDeletedPipelineIds) {
- this.$window.localStorage.setItem(
- RECENTLY_DELETED_PIPELINE_IDS_STORAGE_KEY,
- JSON.stringify(recentlyDeletedPipelineIds)
- );
- }
-}
diff --git a/x-pack/legacy/plugins/logstash/public/services/security/index.js b/x-pack/legacy/plugins/logstash/public/services/security/index.js
deleted file mode 100755
index c9ff911723156..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/security/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { logstashSecurity } from './logstash_security';
diff --git a/x-pack/legacy/plugins/logstash/public/services/upgrade/index.js b/x-pack/legacy/plugins/logstash/public/services/upgrade/index.js
deleted file mode 100755
index 345d0d0ff68c6..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/upgrade/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import './upgrade_service.factory';
diff --git a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.factory.js b/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.factory.js
deleted file mode 100755
index 925c6ae677bdf..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.factory.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { uiModules } from 'ui/modules';
-import { UpgradeService } from './upgrade_service';
-
-uiModules.get('xpack/logstash').factory('upgradeService', $injector => {
- const $http = $injector.get('$http');
- return new UpgradeService($http);
-});
diff --git a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.js b/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.js
deleted file mode 100755
index 2019bdc1bf1aa..0000000000000
--- a/x-pack/legacy/plugins/logstash/public/services/upgrade/upgrade_service.js
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import chrome from 'ui/chrome';
-import { ROUTES } from '../../../../../../plugins/logstash/common/constants';
-
-export class UpgradeService {
- constructor($http) {
- this.$http = $http;
- this.basePath = chrome.addBasePath(ROUTES.API_ROOT);
- }
-
- executeUpgrade() {
- return this.$http
- .post(`${this.basePath}/upgrade`)
- .then(response => response.data.is_upgraded)
- .catch(e => {
- throw e.data.message;
- });
- }
-}
diff --git a/x-pack/legacy/plugins/logstash/server/lib/check_license/__tests__/check_license.js b/x-pack/legacy/plugins/logstash/server/lib/check_license/__tests__/check_license.js
deleted file mode 100755
index 5fcce0aaa1219..0000000000000
--- a/x-pack/legacy/plugins/logstash/server/lib/check_license/__tests__/check_license.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import expect from '@kbn/expect';
-import { set } from 'lodash';
-import { checkLicense } from '../check_license';
-
-describe('check_license', function() {
- let mockLicenseInfo;
- beforeEach(() => (mockLicenseInfo = {}));
-
- describe('license information is undefined', () => {
- beforeEach(() => (mockLicenseInfo = undefined));
-
- it('should set isAvailable to false', () => {
- expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
- });
-
- it('should set enableLinks to false', () => {
- expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
- });
-
- it('should set isReadOnly to false', () => {
- expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false);
- });
-
- it('should set a message', () => {
- expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
- });
- });
-
- describe('license information is not available', () => {
- beforeEach(() => (mockLicenseInfo.isAvailable = () => false));
-
- it('should set isAvailable to false', () => {
- expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
- });
-
- it('should set enableLinks to false', () => {
- expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
- });
-
- it('should set isReadOnly to false', () => {
- expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false);
- });
-
- it('should set a message', () => {
- expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
- });
- });
-
- describe('license information is available', () => {
- beforeEach(() => {
- mockLicenseInfo.isAvailable = () => true;
- set(mockLicenseInfo, 'license.getType', () => 'basic');
- });
-
- describe('& license is > basic', () => {
- beforeEach(() => {
- set(mockLicenseInfo, 'license.isOneOf', () => true);
- mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled
- });
-
- describe('& license is active', () => {
- beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true));
-
- it('should set isAvailable to true', () => {
- expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true);
- });
-
- it('should set enableLinks to true', () => {
- expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true);
- });
-
- it('should set isReadOnly to false', () => {
- expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false);
- });
-
- it('should not set a message', () => {
- expect(checkLicense(mockLicenseInfo).message).to.be(undefined);
- });
- });
-
- describe('& license is expired', () => {
- beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false));
-
- it('should set isAvailable to true', () => {
- expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true);
- });
-
- it('should set enableLinks to true', () => {
- expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true);
- });
-
- it('should set isReadOnly to true', () => {
- expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(true);
- });
-
- it('should set a message', () => {
- expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
- });
- });
- });
-
- describe('& license is basic', () => {
- beforeEach(() => {
- set(mockLicenseInfo, 'license.isOneOf', () => false);
- mockLicenseInfo.feature = () => ({ isEnabled: () => true }); // Security feature is enabled
- });
-
- describe('& license is active', () => {
- beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => true));
-
- it('should set isAvailable to false', () => {
- expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
- });
-
- it('should set enableLinks to false', () => {
- expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
- });
-
- it('should set isReadOnly to false', () => {
- expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false);
- });
-
- it('should set a message', () => {
- expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
- });
- });
-
- describe('& license is expired', () => {
- beforeEach(() => set(mockLicenseInfo, 'license.isActive', () => false));
-
- it('should set isAvailable to false', () => {
- expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
- });
-
- it('should set enableLinks to false', () => {
- expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
- });
-
- it('should set isReadOnly to false', () => {
- expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false);
- });
-
- it('should set a message', () => {
- expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
- });
- });
- });
-
- describe('& security is disabled', () => {
- beforeEach(() => {
- mockLicenseInfo.feature = () => ({ isEnabled: () => false }); // Security feature is disabled
- set(mockLicenseInfo, 'license.isOneOf', () => true);
- set(mockLicenseInfo, 'license.isActive', () => true);
- });
-
- it('should set isAvailable to false', () => {
- expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
- });
-
- it('should set enableLinks to false', () => {
- expect(checkLicense(mockLicenseInfo).enableLinks).to.be(false);
- });
-
- it('should set isReadOnly to false', () => {
- expect(checkLicense(mockLicenseInfo).isReadOnly).to.be(false);
- });
-
- it('should set a message', () => {
- expect(checkLicense(mockLicenseInfo).message).to.not.be(undefined);
- });
- });
- });
-});
diff --git a/x-pack/legacy/plugins/logstash/server/lib/check_license/check_license.js b/x-pack/legacy/plugins/logstash/server/lib/check_license/check_license.js
deleted file mode 100755
index 31136ae1c72a5..0000000000000
--- a/x-pack/legacy/plugins/logstash/server/lib/check_license/check_license.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-
-export function checkLicense(xpackLicenseInfo) {
- // If, for some reason, we cannot get the license information
- // from Elasticsearch, assume worst case and disable the Logstash pipeline UI
- if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) {
- return {
- isAvailable: false,
- enableLinks: false,
- isReadOnly: false,
- message: i18n.translate(
- 'xpack.logstash.managementSection.notPossibleToManagePipelinesMessage',
- {
- defaultMessage:
- 'You cannot manage Logstash pipelines because license information is not available at this time.',
- }
- ),
- };
- }
-
- const VALID_LICENSE_MODES = ['trial', 'standard', 'gold', 'platinum', 'enterprise'];
-
- const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_LICENSE_MODES);
- const isLicenseActive = xpackLicenseInfo.license.isActive();
- const licenseType = xpackLicenseInfo.license.getType();
- const isSecurityEnabled = xpackLicenseInfo.feature('security').isEnabled();
-
- // Security is not enabled in ES
- if (!isSecurityEnabled) {
- const message = i18n.translate('xpack.logstash.managementSection.enableSecurityDescription', {
- defaultMessage:
- 'Security must be enabled in order to use Logstash pipeline management features.' +
- ' Please set xpack.security.enabled: true in your elasticsearch.yml.',
- });
- return {
- isAvailable: false,
- enableLinks: false,
- isReadOnly: false,
- message,
- };
- }
-
- // License is not valid
- if (!isLicenseModeValid) {
- return {
- isAvailable: false,
- enableLinks: false,
- isReadOnly: false,
- message: i18n.translate('xpack.logstash.managementSection.licenseDoesNotSupportDescription', {
- defaultMessage:
- 'Your {licenseType} license does not support Logstash pipeline management features. Please upgrade your license.',
- values: { licenseType },
- }),
- };
- }
-
- // License is valid but not active, we go into a read-only mode.
- if (!isLicenseActive) {
- return {
- isAvailable: true,
- enableLinks: true,
- isReadOnly: true,
- message: i18n.translate(
- 'xpack.logstash.managementSection.pipelineCrudOperationsNotAllowedDescription',
- {
- defaultMessage:
- 'You cannot edit, create, or delete your Logstash pipelines because your {licenseType} license has expired.',
- values: { licenseType },
- }
- ),
- };
- }
-
- // License is valid and active
- return {
- isAvailable: true,
- enableLinks: true,
- isReadOnly: false,
- };
-}
diff --git a/x-pack/legacy/plugins/logstash/server/lib/check_license/index.js b/x-pack/legacy/plugins/logstash/server/lib/check_license/index.js
deleted file mode 100755
index f2c070fd44b6e..0000000000000
--- a/x-pack/legacy/plugins/logstash/server/lib/check_license/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { checkLicense } from './check_license';
diff --git a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/index.js b/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/index.js
deleted file mode 100755
index 7b0f97c38d129..0000000000000
--- a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { registerLicenseChecker } from './register_license_checker';
diff --git a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/register_license_checker.js b/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/register_license_checker.js
deleted file mode 100755
index a0d06e77b410d..0000000000000
--- a/x-pack/legacy/plugins/logstash/server/lib/register_license_checker/register_license_checker.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { mirrorPluginStatus } from '../../../../../server/lib/mirror_plugin_status';
-import { checkLicense } from '../check_license';
-import { PLUGIN } from '../../../../../../plugins/logstash/common/constants';
-
-export function registerLicenseChecker(server) {
- const xpackMainPlugin = server.plugins.xpack_main;
- const logstashPlugin = server.plugins.logstash;
-
- mirrorPluginStatus(xpackMainPlugin, logstashPlugin);
- xpackMainPlugin.status.once('green', () => {
- // Register a function that is called whenever the xpack info changes,
- // to re-compute the license check results for this plugin
- xpackMainPlugin.info.feature(PLUGIN.ID).registerLicenseCheckResultsGenerator(checkLicense);
- });
-}
diff --git a/x-pack/legacy/plugins/maps/common/constants.ts b/x-pack/legacy/plugins/maps/common/constants.ts
deleted file mode 100644
index 98945653c25dc..0000000000000
--- a/x-pack/legacy/plugins/maps/common/constants.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export * from '../../../../plugins/maps/common/constants';
diff --git a/x-pack/legacy/plugins/maps/common/descriptor_types.ts b/x-pack/legacy/plugins/maps/common/descriptor_types.ts
deleted file mode 100644
index 1f0eda26e7f7d..0000000000000
--- a/x-pack/legacy/plugins/maps/common/descriptor_types.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export * from '../../../../plugins/maps/common/descriptor_types';
diff --git a/x-pack/legacy/plugins/maps/common/i18n_getters.ts b/x-pack/legacy/plugins/maps/common/i18n_getters.ts
deleted file mode 100644
index f9d186dea2e2b..0000000000000
--- a/x-pack/legacy/plugins/maps/common/i18n_getters.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export * from '../../../../plugins/maps/common/i18n_getters';
diff --git a/x-pack/legacy/plugins/maps/common/parse_xml_string.js b/x-pack/legacy/plugins/maps/common/parse_xml_string.js
deleted file mode 100644
index 34ec144472828..0000000000000
--- a/x-pack/legacy/plugins/maps/common/parse_xml_string.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export * from '../../../../plugins/maps/common/parse_xml_string';
diff --git a/x-pack/legacy/plugins/maps/index.js b/x-pack/legacy/plugins/maps/index.js
index 1a7f478d3bbad..8546e3712c763 100644
--- a/x-pack/legacy/plugins/maps/index.js
+++ b/x-pack/legacy/plugins/maps/index.js
@@ -9,9 +9,14 @@ import mappings from './mappings.json';
import { i18n } from '@kbn/i18n';
import { resolve } from 'path';
import { migrations } from './migrations';
-import { getAppTitle } from './common/i18n_getters';
+import { getAppTitle } from '../../../plugins/maps/common/i18n_getters';
import { MapPlugin } from './server/plugin';
-import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from './common/constants';
+import {
+ APP_ID,
+ APP_ICON,
+ createMapPath,
+ MAP_SAVED_OBJECT_TYPE,
+} from '../../../plugins/maps/common/constants';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
export function maps(kibana) {
@@ -38,6 +43,7 @@ export function maps(kibana) {
return {
showMapVisualizationTypes: serverConfig.get('xpack.maps.showMapVisualizationTypes'),
showMapsInspectorAdapter: serverConfig.get('xpack.maps.showMapsInspectorAdapter'),
+ enableVectorTiles: serverConfig.get('xpack.maps.enableVectorTiles'),
preserveDrawingBuffer: serverConfig.get('xpack.maps.preserveDrawingBuffer'),
isEmsEnabled: mapConfig.includeElasticMapsService,
emsFontLibraryUrl: mapConfig.emsFontLibraryUrl,
@@ -52,7 +58,6 @@ export function maps(kibana) {
};
},
embeddableFactories: ['plugins/maps/embeddable/map_embeddable_factory'],
- home: ['plugins/maps/legacy_register_feature'],
styleSheetPaths: `${__dirname}/public/index.scss`,
savedObjectSchemas: {
'maps-telemetry': {
@@ -77,7 +82,6 @@ export function maps(kibana) {
},
mappings,
migrations,
- hacks: ['plugins/maps/register_vis_type_alias'],
},
config(Joi) {
return Joi.object({
@@ -85,6 +89,7 @@ export function maps(kibana) {
showMapVisualizationTypes: Joi.boolean().default(false),
showMapsInspectorAdapter: Joi.boolean().default(false), // flag used in functional testing
preserveDrawingBuffer: Joi.boolean().default(false), // flag used in functional testing
+ enableVectorTiles: Joi.boolean().default(false), // flag used to enable/disable vector-tiles
}).default();
},
diff --git a/x-pack/legacy/plugins/maps/migrations.js b/x-pack/legacy/plugins/maps/migrations.js
index 6a1f5bc937497..d3666025082b7 100644
--- a/x-pack/legacy/plugins/maps/migrations.js
+++ b/x-pack/legacy/plugins/maps/migrations.js
@@ -4,13 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { extractReferences } from './common/migrations/references';
-import { emsRasterTileToEmsVectorTile } from './common/migrations/ems_raster_tile_to_ems_vector_tile';
-import { topHitsTimeToSort } from './common/migrations/top_hits_time_to_sort';
-import { moveApplyGlobalQueryToSources } from './common/migrations/move_apply_global_query';
-import { addFieldMetaOptions } from './common/migrations/add_field_meta_options';
-import { migrateSymbolStyleDescriptor } from './common/migrations/migrate_symbol_style_descriptor';
-import { migrateUseTopHitsToScalingType } from './common/migrations/scaling_type';
+import { extractReferences } from '../../../plugins/maps/common/migrations/references';
+import { emsRasterTileToEmsVectorTile } from '../../../plugins/maps/common/migrations/ems_raster_tile_to_ems_vector_tile';
+import { topHitsTimeToSort } from '../../../plugins/maps/common/migrations/top_hits_time_to_sort';
+import { moveApplyGlobalQueryToSources } from '../../../plugins/maps/common/migrations/move_apply_global_query';
+import { addFieldMetaOptions } from '../../../plugins/maps/common/migrations/add_field_meta_options';
+import { migrateSymbolStyleDescriptor } from '../../../plugins/maps/common/migrations/migrate_symbol_style_descriptor';
+import { migrateUseTopHitsToScalingType } from '../../../plugins/maps/common/migrations/scaling_type';
+import { migrateJoinAggKey } from '../../../plugins/maps/common/migrations/join_agg_key';
export const migrations = {
map: {
@@ -57,5 +58,13 @@ export const migrations = {
attributes: attributesPhase2,
};
},
+ '7.8.0': doc => {
+ const attributes = migrateJoinAggKey(doc);
+
+ return {
+ ...doc,
+ attributes,
+ };
+ },
},
};
diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts b/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts
deleted file mode 100644
index 34f8c30b51874..0000000000000
--- a/x-pack/legacy/plugins/maps/public/actions/map_actions.d.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-/* eslint-disable @typescript-eslint/consistent-type-definitions */
-
-export * from '../../../../../plugins/maps/public/actions/map_actions';
diff --git a/x-pack/legacy/plugins/maps/public/actions/map_actions.js b/x-pack/legacy/plugins/maps/public/actions/map_actions.js
deleted file mode 100644
index 7bfbf5761c5b8..0000000000000
--- a/x-pack/legacy/plugins/maps/public/actions/map_actions.js
+++ /dev/null
@@ -1,978 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import _ from 'lodash';
-import turf from 'turf';
-import turfBooleanContains from '@turf/boolean-contains';
-import uuid from 'uuid/v4';
-import {
- getLayerList,
- getLayerListRaw,
- getDataFilters,
- getSelectedLayerId,
- getMapReady,
- getWaitingForMapReadyLayerListRaw,
- getTransientLayerId,
- getOpenTooltips,
- getQuery,
- getDataRequestDescriptor,
-} from '../selectors/map_selectors';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { FLYOUT_STATE } from '../../../../../plugins/maps/public/reducers/ui';
-import {
- cancelRequest,
- registerCancelCallback,
- unregisterCancelCallback,
- getEventHandlers,
- // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-} from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
-import { updateFlyout } from '../actions/ui_actions';
-import {
- FEATURE_ID_PROPERTY_NAME,
- LAYER_TYPE,
- SOURCE_DATA_ID_ORIGIN,
-} from '../../common/constants';
-
-import {
- SET_SELECTED_LAYER,
- SET_TRANSIENT_LAYER,
- UPDATE_LAYER_ORDER,
- ADD_LAYER,
- SET_LAYER_ERROR_STATUS,
- ADD_WAITING_FOR_MAP_READY_LAYER,
- CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST,
- REMOVE_LAYER,
- SET_LAYER_VISIBILITY,
- MAP_EXTENT_CHANGED,
- MAP_READY,
- MAP_DESTROYED,
- LAYER_DATA_LOAD_STARTED,
- LAYER_DATA_LOAD_ENDED,
- LAYER_DATA_LOAD_ERROR,
- UPDATE_SOURCE_DATA_REQUEST,
- SET_JOINS,
- SET_QUERY,
- TRIGGER_REFRESH_TIMER,
- UPDATE_LAYER_PROP,
- UPDATE_LAYER_STYLE,
- SET_LAYER_STYLE_META,
- UPDATE_SOURCE_PROP,
- SET_REFRESH_CONFIG,
- SET_MOUSE_COORDINATES,
- CLEAR_MOUSE_COORDINATES,
- SET_GOTO,
- CLEAR_GOTO,
- TRACK_CURRENT_LAYER_STATE,
- ROLLBACK_TO_TRACKED_LAYER_STATE,
- REMOVE_TRACKED_LAYER_STATE,
- SET_OPEN_TOOLTIPS,
- UPDATE_DRAW_STATE,
- SET_SCROLL_ZOOM,
- SET_MAP_INIT_ERROR,
- SET_INTERACTIVE,
- DISABLE_TOOLTIP_CONTROL,
- HIDE_TOOLBAR_OVERLAY,
- HIDE_LAYER_CONTROL,
- HIDE_VIEW_CONTROL,
- SET_WAITING_FOR_READY_HIDDEN_LAYERS,
- // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-} from '../../../../../plugins/maps/public/actions/map_actions';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export * from '../../../../../plugins/maps/public/actions/map_actions';
-
-function getLayerLoadingCallbacks(dispatch, getState, layerId) {
- return {
- startLoading: (dataId, requestToken, meta) =>
- dispatch(startDataLoad(layerId, dataId, requestToken, meta)),
- stopLoading: (dataId, requestToken, data, meta) =>
- dispatch(endDataLoad(layerId, dataId, requestToken, data, meta)),
- onLoadError: (dataId, requestToken, errorMessage) =>
- dispatch(onDataLoadError(layerId, dataId, requestToken, errorMessage)),
- updateSourceData: newData => {
- dispatch(updateSourceDataRequest(layerId, newData));
- },
- isRequestStillActive: (dataId, requestToken) => {
- const dataRequest = getDataRequestDescriptor(getState(), layerId, dataId);
- if (!dataRequest) {
- return false;
- }
- return dataRequest.dataRequestToken === requestToken;
- },
- registerCancelCallback: (requestToken, callback) =>
- dispatch(registerCancelCallback(requestToken, callback)),
- };
-}
-
-function getLayerById(layerId, state) {
- return getLayerList(state).find(layer => {
- return layerId === layer.getId();
- });
-}
-
-async function syncDataForAllLayers(dispatch, getState, dataFilters) {
- const state = getState();
- const layerList = getLayerList(state);
- const syncs = layerList.map(layer => {
- const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layer.getId());
- return layer.syncData({ ...loadingFunctions, dataFilters });
- });
- await Promise.all(syncs);
-}
-
-export function cancelAllInFlightRequests() {
- return (dispatch, getState) => {
- getLayerList(getState()).forEach(layer => {
- dispatch(clearDataRequests(layer));
- });
- };
-}
-
-function clearDataRequests(layer) {
- return dispatch => {
- layer.getInFlightRequestTokens().forEach(requestToken => {
- dispatch(cancelRequest(requestToken));
- });
- dispatch({
- type: UPDATE_LAYER_PROP,
- id: layer.getId(),
- propName: '__dataRequests',
- newValue: [],
- });
- };
-}
-
-export function setMapInitError(errorMessage) {
- return {
- type: SET_MAP_INIT_ERROR,
- errorMessage,
- };
-}
-
-export function trackCurrentLayerState(layerId) {
- return {
- type: TRACK_CURRENT_LAYER_STATE,
- layerId: layerId,
- };
-}
-
-export function rollbackToTrackedLayerStateForSelectedLayer() {
- return async (dispatch, getState) => {
- const layerId = getSelectedLayerId(getState());
- await dispatch({
- type: ROLLBACK_TO_TRACKED_LAYER_STATE,
- layerId: layerId,
- });
-
- // Ensure updateStyleMeta is triggered
- // syncDataForLayer may not trigger endDataLoad if no re-fetch is required
- dispatch(updateStyleMeta(layerId));
-
- dispatch(syncDataForLayer(layerId));
- };
-}
-
-export function removeTrackedLayerStateForSelectedLayer() {
- return (dispatch, getState) => {
- const layerId = getSelectedLayerId(getState());
- dispatch({
- type: REMOVE_TRACKED_LAYER_STATE,
- layerId: layerId,
- });
- };
-}
-
-export function replaceLayerList(newLayerList) {
- return (dispatch, getState) => {
- const isMapReady = getMapReady(getState());
- if (!isMapReady) {
- dispatch({
- type: CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST,
- });
- } else {
- getLayerListRaw(getState()).forEach(({ id }) => {
- dispatch(removeLayerFromLayerList(id));
- });
- }
-
- newLayerList.forEach(layerDescriptor => {
- dispatch(addLayer(layerDescriptor));
- });
- };
-}
-
-export function cloneLayer(layerId) {
- return async (dispatch, getState) => {
- const layer = getLayerById(layerId, getState());
- if (!layer) {
- return;
- }
-
- const clonedDescriptor = await layer.cloneDescriptor();
- dispatch(addLayer(clonedDescriptor));
- };
-}
-
-export function addLayer(layerDescriptor) {
- return (dispatch, getState) => {
- const isMapReady = getMapReady(getState());
- if (!isMapReady) {
- dispatch({
- type: ADD_WAITING_FOR_MAP_READY_LAYER,
- layer: layerDescriptor,
- });
- return;
- }
-
- dispatch({
- type: ADD_LAYER,
- layer: layerDescriptor,
- });
- dispatch(syncDataForLayer(layerDescriptor.id));
- };
-}
-
-// Do not use when rendering a map. Method exists to enable selectors for getLayerList when
-// rendering is not needed.
-export function addLayerWithoutDataSync(layerDescriptor) {
- return {
- type: ADD_LAYER,
- layer: layerDescriptor,
- };
-}
-
-function setLayerDataLoadErrorStatus(layerId, errorMessage) {
- return dispatch => {
- dispatch({
- type: SET_LAYER_ERROR_STATUS,
- isInErrorState: errorMessage !== null,
- layerId,
- errorMessage,
- });
- };
-}
-
-export function cleanTooltipStateForLayer(layerId, layerFeatures = []) {
- return (dispatch, getState) => {
- let featuresRemoved = false;
- const openTooltips = getOpenTooltips(getState())
- .map(tooltipState => {
- const nextFeatures = tooltipState.features.filter(tooltipFeature => {
- if (tooltipFeature.layerId !== layerId) {
- // feature from another layer, keep it
- return true;
- }
-
- // Keep feature if it is still in layer
- return layerFeatures.some(layerFeature => {
- return layerFeature.properties[FEATURE_ID_PROPERTY_NAME] === tooltipFeature.id;
- });
- });
-
- if (tooltipState.features.length !== nextFeatures.length) {
- featuresRemoved = true;
- }
-
- return { ...tooltipState, features: nextFeatures };
- })
- .filter(tooltipState => {
- return tooltipState.features.length > 0;
- });
-
- if (featuresRemoved) {
- dispatch({
- type: SET_OPEN_TOOLTIPS,
- openTooltips,
- });
- }
- };
-}
-
-export function setLayerVisibility(layerId, makeVisible) {
- return async (dispatch, getState) => {
- //if the current-state is invisible, we also want to sync data
- //e.g. if a layer was invisible at start-up, it won't have any data loaded
- const layer = getLayerById(layerId, getState());
-
- // If the layer visibility is already what we want it to be, do nothing
- if (!layer || layer.isVisible() === makeVisible) {
- return;
- }
-
- if (!makeVisible) {
- dispatch(cleanTooltipStateForLayer(layerId));
- }
-
- await dispatch({
- type: SET_LAYER_VISIBILITY,
- layerId,
- visibility: makeVisible,
- });
- if (makeVisible) {
- dispatch(syncDataForLayer(layerId));
- }
- };
-}
-
-export function toggleLayerVisible(layerId) {
- return async (dispatch, getState) => {
- const layer = getLayerById(layerId, getState());
- if (!layer) {
- return;
- }
- const makeVisible = !layer.isVisible();
-
- dispatch(setLayerVisibility(layerId, makeVisible));
- };
-}
-
-export function setSelectedLayer(layerId) {
- return async (dispatch, getState) => {
- const oldSelectedLayer = getSelectedLayerId(getState());
- if (oldSelectedLayer) {
- await dispatch(rollbackToTrackedLayerStateForSelectedLayer());
- }
- if (layerId) {
- dispatch(trackCurrentLayerState(layerId));
- }
- dispatch({
- type: SET_SELECTED_LAYER,
- selectedLayerId: layerId,
- });
- };
-}
-
-export function removeTransientLayer() {
- return async (dispatch, getState) => {
- const transientLayerId = getTransientLayerId(getState());
- if (transientLayerId) {
- await dispatch(removeLayerFromLayerList(transientLayerId));
- await dispatch(setTransientLayer(null));
- }
- };
-}
-
-export function setTransientLayer(layerId) {
- return {
- type: SET_TRANSIENT_LAYER,
- transientLayerId: layerId,
- };
-}
-
-export function clearTransientLayerStateAndCloseFlyout() {
- return async dispatch => {
- await dispatch(updateFlyout(FLYOUT_STATE.NONE));
- await dispatch(setSelectedLayer(null));
- await dispatch(removeTransientLayer());
- };
-}
-
-export function updateLayerOrder(newLayerOrder) {
- return {
- type: UPDATE_LAYER_ORDER,
- newLayerOrder,
- };
-}
-
-export function mapReady() {
- return (dispatch, getState) => {
- dispatch({
- type: MAP_READY,
- });
-
- getWaitingForMapReadyLayerListRaw(getState()).forEach(layerDescriptor => {
- dispatch(addLayer(layerDescriptor));
- });
-
- dispatch({
- type: CLEAR_WAITING_FOR_MAP_READY_LAYER_LIST,
- });
- };
-}
-
-export function mapDestroyed() {
- return {
- type: MAP_DESTROYED,
- };
-}
-
-export function mapExtentChanged(newMapConstants) {
- return async (dispatch, getState) => {
- const state = getState();
- const dataFilters = getDataFilters(state);
- const { extent, zoom: newZoom } = newMapConstants;
- const { buffer, zoom: currentZoom } = dataFilters;
-
- if (extent) {
- let doesBufferContainExtent = false;
- if (buffer) {
- const bufferGeometry = turf.bboxPolygon([
- buffer.minLon,
- buffer.minLat,
- buffer.maxLon,
- buffer.maxLat,
- ]);
- const extentGeometry = turf.bboxPolygon([
- extent.minLon,
- extent.minLat,
- extent.maxLon,
- extent.maxLat,
- ]);
-
- doesBufferContainExtent = turfBooleanContains(bufferGeometry, extentGeometry);
- }
-
- if (!doesBufferContainExtent || currentZoom !== newZoom) {
- const scaleFactor = 0.5; // TODO put scale factor in store and fetch with selector
- const width = extent.maxLon - extent.minLon;
- const height = extent.maxLat - extent.minLat;
- dataFilters.buffer = {
- minLon: extent.minLon - width * scaleFactor,
- minLat: extent.minLat - height * scaleFactor,
- maxLon: extent.maxLon + width * scaleFactor,
- maxLat: extent.maxLat + height * scaleFactor,
- };
- }
- }
-
- dispatch({
- type: MAP_EXTENT_CHANGED,
- mapState: {
- ...dataFilters,
- ...newMapConstants,
- },
- });
- const newDataFilters = { ...dataFilters, ...newMapConstants };
- await syncDataForAllLayers(dispatch, getState, newDataFilters);
- };
-}
-
-export function closeOnClickTooltip(tooltipId) {
- return (dispatch, getState) => {
- dispatch({
- type: SET_OPEN_TOOLTIPS,
- openTooltips: getOpenTooltips(getState()).filter(({ id }) => {
- return tooltipId !== id;
- }),
- });
- };
-}
-
-export function openOnClickTooltip(tooltipState) {
- return (dispatch, getState) => {
- const openTooltips = getOpenTooltips(getState()).filter(({ features, location, isLocked }) => {
- return (
- isLocked &&
- !_.isEqual(location, tooltipState.location) &&
- !_.isEqual(features, tooltipState.features)
- );
- });
-
- openTooltips.push({
- ...tooltipState,
- isLocked: true,
- id: uuid(),
- });
-
- dispatch({
- type: SET_OPEN_TOOLTIPS,
- openTooltips,
- });
- };
-}
-
-export function closeOnHoverTooltip() {
- return (dispatch, getState) => {
- if (getOpenTooltips(getState()).length) {
- dispatch({
- type: SET_OPEN_TOOLTIPS,
- openTooltips: [],
- });
- }
- };
-}
-
-export function openOnHoverTooltip(tooltipState) {
- return {
- type: SET_OPEN_TOOLTIPS,
- openTooltips: [
- {
- ...tooltipState,
- isLocked: false,
- id: uuid(),
- },
- ],
- };
-}
-
-export function setMouseCoordinates({ lat, lon }) {
- let safeLon = lon;
- if (lon > 180) {
- const overlapWestOfDateLine = lon - 180;
- safeLon = -180 + overlapWestOfDateLine;
- } else if (lon < -180) {
- const overlapEastOfDateLine = Math.abs(lon) - 180;
- safeLon = 180 - overlapEastOfDateLine;
- }
-
- return {
- type: SET_MOUSE_COORDINATES,
- lat,
- lon: safeLon,
- };
-}
-
-export function clearMouseCoordinates() {
- return { type: CLEAR_MOUSE_COORDINATES };
-}
-
-export function disableScrollZoom() {
- return { type: SET_SCROLL_ZOOM, scrollZoom: false };
-}
-
-export function fitToLayerExtent(layerId) {
- return async function(dispatch, getState) {
- const targetLayer = getLayerById(layerId, getState());
-
- if (targetLayer) {
- const dataFilters = getDataFilters(getState());
- const bounds = await targetLayer.getBounds(dataFilters);
- if (bounds) {
- await dispatch(setGotoWithBounds(bounds));
- }
- }
- };
-}
-
-export function setGotoWithBounds(bounds) {
- return {
- type: SET_GOTO,
- bounds: bounds,
- };
-}
-
-export function setGotoWithCenter({ lat, lon, zoom }) {
- return {
- type: SET_GOTO,
- center: { lat, lon, zoom },
- };
-}
-
-export function clearGoto() {
- return { type: CLEAR_GOTO };
-}
-
-export function startDataLoad(layerId, dataId, requestToken, meta = {}) {
- return (dispatch, getState) => {
- const layer = getLayerById(layerId, getState());
- if (layer) {
- dispatch(cancelRequest(layer.getPrevRequestToken(dataId)));
- }
-
- const eventHandlers = getEventHandlers(getState());
- if (eventHandlers && eventHandlers.onDataLoad) {
- eventHandlers.onDataLoad({
- layerId,
- dataId,
- });
- }
-
- dispatch({
- meta,
- type: LAYER_DATA_LOAD_STARTED,
- layerId,
- dataId,
- requestToken,
- });
- };
-}
-
-export function updateSourceDataRequest(layerId, newData) {
- return dispatch => {
- dispatch({
- type: UPDATE_SOURCE_DATA_REQUEST,
- dataId: SOURCE_DATA_ID_ORIGIN,
- layerId,
- newData,
- });
-
- dispatch(updateStyleMeta(layerId));
- };
-}
-
-export function endDataLoad(layerId, dataId, requestToken, data, meta) {
- return async (dispatch, getState) => {
- dispatch(unregisterCancelCallback(requestToken));
-
- const features = data && data.features ? data.features : [];
-
- const eventHandlers = getEventHandlers(getState());
- if (eventHandlers && eventHandlers.onDataLoadEnd) {
- const layer = getLayerById(layerId, getState());
- const resultMeta = {};
- if (layer && layer.getType() === LAYER_TYPE.VECTOR) {
- resultMeta.featuresCount = features.length;
- }
-
- eventHandlers.onDataLoadEnd({
- layerId,
- dataId,
- resultMeta,
- });
- }
-
- dispatch(cleanTooltipStateForLayer(layerId, features));
- dispatch({
- type: LAYER_DATA_LOAD_ENDED,
- layerId,
- dataId,
- data,
- meta,
- requestToken,
- });
-
- //Clear any data-load errors when there is a succesful data return.
- //Co this on end-data-load iso at start-data-load to avoid blipping the error status between true/false.
- //This avoids jitter in the warning icon of the TOC when the requests continues to return errors.
- dispatch(setLayerDataLoadErrorStatus(layerId, null));
-
- dispatch(updateStyleMeta(layerId));
- };
-}
-
-export function onDataLoadError(layerId, dataId, requestToken, errorMessage) {
- return async (dispatch, getState) => {
- dispatch(unregisterCancelCallback(requestToken));
-
- const eventHandlers = getEventHandlers(getState());
- if (eventHandlers && eventHandlers.onDataLoadError) {
- eventHandlers.onDataLoadError({
- layerId,
- dataId,
- errorMessage,
- });
- }
-
- dispatch(cleanTooltipStateForLayer(layerId));
- dispatch({
- type: LAYER_DATA_LOAD_ERROR,
- data: null,
- layerId,
- dataId,
- requestToken,
- });
-
- dispatch(setLayerDataLoadErrorStatus(layerId, errorMessage));
- };
-}
-
-export function updateSourceProp(layerId, propName, value, newLayerType) {
- return async dispatch => {
- dispatch({
- type: UPDATE_SOURCE_PROP,
- layerId,
- propName,
- value,
- });
- if (newLayerType) {
- dispatch(updateLayerType(layerId, newLayerType));
- }
- await dispatch(clearMissingStyleProperties(layerId));
- dispatch(syncDataForLayer(layerId));
- };
-}
-
-function updateLayerType(layerId, newLayerType) {
- return (dispatch, getState) => {
- const layer = getLayerById(layerId, getState());
- if (!layer || layer.getType() === newLayerType) {
- return;
- }
- dispatch(clearDataRequests(layer));
- dispatch({
- type: UPDATE_LAYER_PROP,
- id: layerId,
- propName: 'type',
- newValue: newLayerType,
- });
- };
-}
-
-export function syncDataForLayer(layerId) {
- return async (dispatch, getState) => {
- const targetLayer = getLayerById(layerId, getState());
- if (targetLayer) {
- const dataFilters = getDataFilters(getState());
- const loadingFunctions = getLayerLoadingCallbacks(dispatch, getState, layerId);
- await targetLayer.syncData({
- ...loadingFunctions,
- dataFilters,
- });
- }
- };
-}
-
-export function updateLayerLabel(id, newLabel) {
- return {
- type: UPDATE_LAYER_PROP,
- id,
- propName: 'label',
- newValue: newLabel,
- };
-}
-
-export function updateLayerMinZoom(id, minZoom) {
- return {
- type: UPDATE_LAYER_PROP,
- id,
- propName: 'minZoom',
- newValue: minZoom,
- };
-}
-
-export function updateLayerMaxZoom(id, maxZoom) {
- return {
- type: UPDATE_LAYER_PROP,
- id,
- propName: 'maxZoom',
- newValue: maxZoom,
- };
-}
-
-export function updateLayerAlpha(id, alpha) {
- return {
- type: UPDATE_LAYER_PROP,
- id,
- propName: 'alpha',
- newValue: alpha,
- };
-}
-
-export function setLayerQuery(id, query) {
- return dispatch => {
- dispatch({
- type: UPDATE_LAYER_PROP,
- id,
- propName: 'query',
- newValue: query,
- });
-
- dispatch(syncDataForLayer(id));
- };
-}
-
-export function removeSelectedLayer() {
- return (dispatch, getState) => {
- const state = getState();
- const layerId = getSelectedLayerId(state);
- dispatch(removeLayer(layerId));
- };
-}
-
-export function removeLayer(layerId) {
- return async (dispatch, getState) => {
- const state = getState();
- const selectedLayerId = getSelectedLayerId(state);
- if (layerId === selectedLayerId) {
- dispatch(updateFlyout(FLYOUT_STATE.NONE));
- await dispatch(setSelectedLayer(null));
- }
- dispatch(removeLayerFromLayerList(layerId));
- };
-}
-
-function removeLayerFromLayerList(layerId) {
- return (dispatch, getState) => {
- const layerGettingRemoved = getLayerById(layerId, getState());
- if (!layerGettingRemoved) {
- return;
- }
-
- layerGettingRemoved.getInFlightRequestTokens().forEach(requestToken => {
- dispatch(cancelRequest(requestToken));
- });
- dispatch(cleanTooltipStateForLayer(layerId));
- layerGettingRemoved.destroy();
- dispatch({
- type: REMOVE_LAYER,
- id: layerId,
- });
- };
-}
-
-export function setQuery({ query, timeFilters, filters = [], refresh = false }) {
- function generateQueryTimestamp() {
- return new Date().toISOString();
- }
- return async (dispatch, getState) => {
- const prevQuery = getQuery(getState());
- const prevTriggeredAt =
- prevQuery && prevQuery.queryLastTriggeredAt
- ? prevQuery.queryLastTriggeredAt
- : generateQueryTimestamp();
-
- dispatch({
- type: SET_QUERY,
- timeFilters,
- query: {
- ...query,
- // ensure query changes to trigger re-fetch when "Refresh" clicked
- queryLastTriggeredAt: refresh ? generateQueryTimestamp() : prevTriggeredAt,
- },
- filters,
- });
-
- const dataFilters = getDataFilters(getState());
- await syncDataForAllLayers(dispatch, getState, dataFilters);
- };
-}
-
-export function setRefreshConfig({ isPaused, interval }) {
- return {
- type: SET_REFRESH_CONFIG,
- isPaused,
- interval,
- };
-}
-
-export function triggerRefreshTimer() {
- return async (dispatch, getState) => {
- dispatch({
- type: TRIGGER_REFRESH_TIMER,
- });
-
- const dataFilters = getDataFilters(getState());
- await syncDataForAllLayers(dispatch, getState, dataFilters);
- };
-}
-
-export function clearMissingStyleProperties(layerId) {
- return async (dispatch, getState) => {
- const targetLayer = getLayerById(layerId, getState());
- if (!targetLayer) {
- return;
- }
-
- const style = targetLayer.getCurrentStyle();
- if (!style) {
- return;
- }
-
- const nextFields = await targetLayer.getFields(); //take into account all fields, since labels can be driven by any field (source or join)
- const { hasChanges, nextStyleDescriptor } = style.getDescriptorWithMissingStylePropsRemoved(
- nextFields
- );
- if (hasChanges) {
- dispatch(updateLayerStyle(layerId, nextStyleDescriptor));
- }
- };
-}
-
-export function updateLayerStyle(layerId, styleDescriptor) {
- return dispatch => {
- dispatch({
- type: UPDATE_LAYER_STYLE,
- layerId,
- style: {
- ...styleDescriptor,
- },
- });
-
- // Ensure updateStyleMeta is triggered
- // syncDataForLayer may not trigger endDataLoad if no re-fetch is required
- dispatch(updateStyleMeta(layerId));
-
- // Style update may require re-fetch, for example ES search may need to retrieve field used for dynamic styling
- dispatch(syncDataForLayer(layerId));
- };
-}
-
-export function updateStyleMeta(layerId) {
- return async (dispatch, getState) => {
- const layer = getLayerById(layerId, getState());
- if (!layer) {
- return;
- }
- const sourceDataRequest = layer.getSourceDataRequest();
- const style = layer.getCurrentStyle();
- if (!style || !sourceDataRequest) {
- return;
- }
- const styleMeta = await style.pluckStyleMetaFromSourceDataRequest(sourceDataRequest);
- dispatch({
- type: SET_LAYER_STYLE_META,
- layerId,
- styleMeta,
- });
- };
-}
-
-export function updateLayerStyleForSelectedLayer(styleDescriptor) {
- return (dispatch, getState) => {
- const selectedLayerId = getSelectedLayerId(getState());
- if (!selectedLayerId) {
- return;
- }
- dispatch(updateLayerStyle(selectedLayerId, styleDescriptor));
- };
-}
-
-export function setJoinsForLayer(layer, joins) {
- return async dispatch => {
- await dispatch({
- type: SET_JOINS,
- layer: layer,
- joins: joins,
- });
-
- await dispatch(clearMissingStyleProperties(layer.getId()));
- dispatch(syncDataForLayer(layer.getId()));
- };
-}
-
-export function updateDrawState(drawState) {
- return dispatch => {
- if (drawState !== null) {
- dispatch({ type: SET_OPEN_TOOLTIPS, openTooltips: [] }); // tooltips just get in the way
- }
- dispatch({
- type: UPDATE_DRAW_STATE,
- drawState: drawState,
- });
- };
-}
-
-export function disableInteractive() {
- return { type: SET_INTERACTIVE, disableInteractive: true };
-}
-
-export function disableTooltipControl() {
- return { type: DISABLE_TOOLTIP_CONTROL, disableTooltipControl: true };
-}
-
-export function hideToolbarOverlay() {
- return { type: HIDE_TOOLBAR_OVERLAY, hideToolbarOverlay: true };
-}
-
-export function hideLayerControl() {
- return { type: HIDE_LAYER_CONTROL, hideLayerControl: true };
-}
-export function hideViewControl() {
- return { type: HIDE_VIEW_CONTROL, hideViewControl: true };
-}
-
-export function setHiddenLayers(hiddenLayerIds) {
- return (dispatch, getState) => {
- const isMapReady = getMapReady(getState());
-
- if (!isMapReady) {
- dispatch({ type: SET_WAITING_FOR_READY_HIDDEN_LAYERS, hiddenLayerIds });
- } else {
- getLayerListRaw(getState()).forEach(layer =>
- dispatch(setLayerVisibility(layer.id, !hiddenLayerIds.includes(layer.id)))
- );
- }
- };
-}
diff --git a/x-pack/legacy/plugins/maps/public/actions/ui_actions.js b/x-pack/legacy/plugins/maps/public/actions/ui_actions.js
deleted file mode 100644
index 33ab2fd74122a..0000000000000
--- a/x-pack/legacy/plugins/maps/public/actions/ui_actions.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import {
- UPDATE_FLYOUT,
- CLOSE_SET_VIEW,
- OPEN_SET_VIEW,
- SET_IS_LAYER_TOC_OPEN,
- SET_FULL_SCREEN,
- SET_READ_ONLY,
- SET_OPEN_TOC_DETAILS,
- SHOW_TOC_DETAILS,
- HIDE_TOC_DETAILS,
- UPDATE_INDEXING_STAGE,
- // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-} from '../../../../../plugins/maps/public/actions/ui_actions';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-export * from '../../../../../plugins/maps/public/actions/ui_actions';
-
-export function exitFullScreen() {
- return {
- type: SET_FULL_SCREEN,
- isFullScreen: false,
- };
-}
-
-export function updateFlyout(display) {
- return {
- type: UPDATE_FLYOUT,
- display,
- };
-}
-export function closeSetView() {
- return {
- type: CLOSE_SET_VIEW,
- };
-}
-export function openSetView() {
- return {
- type: OPEN_SET_VIEW,
- };
-}
-export function setIsLayerTOCOpen(isLayerTOCOpen) {
- return {
- type: SET_IS_LAYER_TOC_OPEN,
- isLayerTOCOpen,
- };
-}
-export function enableFullScreen() {
- return {
- type: SET_FULL_SCREEN,
- isFullScreen: true,
- };
-}
-export function setReadOnly(isReadOnly) {
- return {
- type: SET_READ_ONLY,
- isReadOnly,
- };
-}
-
-export function setOpenTOCDetails(layerIds) {
- return {
- type: SET_OPEN_TOC_DETAILS,
- layerIds,
- };
-}
-
-export function showTOCDetails(layerId) {
- return {
- type: SHOW_TOC_DETAILS,
- layerId,
- };
-}
-
-export function hideTOCDetails(layerId) {
- return {
- type: HIDE_TOC_DETAILS,
- layerId,
- };
-}
-
-export function updateIndexingStage(stage) {
- return {
- type: UPDATE_INDEXING_STAGE,
- stage,
- };
-}
diff --git a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js b/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js
deleted file mode 100644
index 686259aeaaba4..0000000000000
--- a/x-pack/legacy/plugins/maps/public/angular/get_initial_layers.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-import _ from 'lodash';
-// Import each layer type, even those not used, to init in registry
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../../plugins/maps/public/layers/sources/wms_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../../plugins/maps/public/layers/sources/ems_file_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../../plugins/maps/public/layers/sources/es_search_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../../plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../../plugins/maps/public/layers/sources/kibana_regionmap_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../../plugins/maps/public/layers/sources/es_geo_grid_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../../plugins/maps/public/layers/sources/xyz_tms_source';
-
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { KibanaTilemapSource } from '../../../../../plugins/maps/public/layers/sources/kibana_tilemap_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { EMSTMSSource } from '../../../../../plugins/maps/public/layers/sources/ems_tms_source';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { getInjectedVarFunc } from '../../../../../plugins/maps/public/kibana_services';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { getKibanaTileMap } from '../../../../../plugins/maps/public/meta';
-
-export function getInitialLayers(layerListJSON, initialLayers = []) {
- if (layerListJSON) {
- return JSON.parse(layerListJSON);
- }
-
- const tilemapSourceFromKibana = getKibanaTileMap();
- if (_.get(tilemapSourceFromKibana, 'url')) {
- const sourceDescriptor = KibanaTilemapSource.createDescriptor();
- const source = new KibanaTilemapSource(sourceDescriptor);
- const layer = source.createDefaultLayer();
- return [layer.toLayerDescriptor(), ...initialLayers];
- }
-
- const isEmsEnabled = getInjectedVarFunc()('isEmsEnabled', true);
- if (isEmsEnabled) {
- const descriptor = EMSTMSSource.createDescriptor({ isAutoSelect: true });
- const source = new EMSTMSSource(descriptor);
- const layer = source.createDefaultLayer();
- return [layer.toLayerDescriptor(), ...initialLayers];
- }
-
- return initialLayers;
-}
diff --git a/x-pack/legacy/plugins/maps/public/angular/map_controller.js b/x-pack/legacy/plugins/maps/public/angular/map_controller.js
index 6bc8a4d0be5ac..9522fd12ad37d 100644
--- a/x-pack/legacy/plugins/maps/public/angular/map_controller.js
+++ b/x-pack/legacy/plugins/maps/public/angular/map_controller.js
@@ -28,8 +28,10 @@ import {
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { createMapStore } from '../../../../../plugins/maps/public/reducers/store';
import { Provider } from 'react-redux';
-import { GisMap } from '../connected_components/gis_map';
-import { addHelpMenuToAppChrome } from '../help_menu_util';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { GisMap } from '../../../../../plugins/maps/public/connected_components/gis_map';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { addHelpMenuToAppChrome } from '../../../../../plugins/maps/public/help_menu_util';
import {
setSelectedLayer,
setRefreshConfig,
@@ -37,7 +39,8 @@ import {
replaceLayerList,
setQuery,
clearTransientLayerStateAndCloseFlyout,
-} from '../actions/map_actions';
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../plugins/maps/public/actions/map_actions';
import {
DEFAULT_IS_LAYER_TOC_OPEN,
FLYOUT_STATE,
@@ -49,22 +52,29 @@ import {
setReadOnly,
setIsLayerTOCOpen,
setOpenTOCDetails,
-} from '../actions/ui_actions';
-import { getIsFullScreen } from '../selectors/ui_selectors';
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../plugins/maps/public/actions/ui_actions';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getIsFullScreen } from '../../../../../plugins/maps/public/selectors/ui_selectors';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { copyPersistentState } from '../../../../../plugins/maps/public/reducers/util';
import {
getQueryableUniqueIndexPatternIds,
hasDirtyState,
getLayerListRaw,
-} from '../selectors/map_selectors';
+ // eslint-disable-next-line @kbn/eslint/no-restricted-paths
+} from '../../../../../plugins/maps/public/selectors/map_selectors';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances';
-import { getInitialLayers } from './get_initial_layers';
-import { getInitialQuery } from './get_initial_query';
-import { getInitialTimeFilters } from './get_initial_time_filters';
-import { getInitialRefreshConfig } from './get_initial_refresh_config';
-import { MAP_SAVED_OBJECT_TYPE, MAP_APP_PATH } from '../../common/constants';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getInitialLayers } from '../../../../../plugins/maps/public/angular/get_initial_layers';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getInitialQuery } from '../../../../../plugins/maps/public/angular/get_initial_query';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getInitialTimeFilters } from '../../../../../plugins/maps/public/angular/get_initial_time_filters';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getInitialRefreshConfig } from '../../../../../plugins/maps/public/angular/get_initial_refresh_config';
+import { MAP_SAVED_OBJECT_TYPE, MAP_APP_PATH } from '../../../../../plugins/maps/common/constants';
import { npSetup, npStart } from 'ui/new_platform';
import { esFilters } from '../../../../../../src/plugins/data/public';
import {
diff --git a/x-pack/legacy/plugins/maps/public/components/_index.scss b/x-pack/legacy/plugins/maps/public/components/_index.scss
deleted file mode 100644
index 0b32719442424..0000000000000
--- a/x-pack/legacy/plugins/maps/public/components/_index.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-@import './metric_editors';
-@import './geometry_filter';
-@import './tooltip_selector';
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/_index.scss
deleted file mode 100644
index 99a2e222ea6c1..0000000000000
--- a/x-pack/legacy/plugins/maps/public/connected_components/_index.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-@import './gis_map/gis_map';
-@import './layer_addpanel/source_select/index';
-@import './layer_panel/index';
-@import './widget_overlay/index';
-@import './toolbar_overlay/index';
-@import './map/features_tooltip/index';
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss
deleted file mode 100644
index 7fe1396fcca16..0000000000000
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/_index.scss
+++ /dev/null
@@ -1 +0,0 @@
-@import './source_select';
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_index.scss
deleted file mode 100644
index fd074edf032fa..0000000000000
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/_index.scss
+++ /dev/null
@@ -1,4 +0,0 @@
-@import './layer_panel';
-@import './filter_editor/filter_editor';
-@import './join_editor/resources/join';
-@import './style_settings/style_settings';
\ No newline at end of file
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts b/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts
deleted file mode 100644
index cf4fdc7be70c6..0000000000000
--- a/x-pack/legacy/plugins/maps/public/connected_components/layer_panel/view.d.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-/* eslint-disable @typescript-eslint/consistent-type-definitions */
-
-export * from '../../../../../../plugins/maps/public/connected_components/layer_panel/view';
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js b/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js
deleted file mode 100644
index a1d1341b7c4f7..0000000000000
--- a/x-pack/legacy/plugins/maps/public/connected_components/map/mb/utils.js
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import _ from 'lodash';
-import {
- loadSpriteSheetImageData,
- addSpriteSheetToMapFromImageData,
- // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-} from '../../../../../../../plugins/maps/public/connected_components/map/mb/utils';
-
-export { loadSpriteSheetImageData, addSpriteSheetToMapFromImageData };
-
-export function removeOrphanedSourcesAndLayers(mbMap, layerList) {
- const mbStyle = mbMap.getStyle();
-
- const mbLayerIdsToRemove = [];
- mbStyle.layers.forEach(mbLayer => {
- const layer = layerList.find(layer => {
- return layer.ownsMbLayerId(mbLayer.id);
- });
- if (!layer) {
- mbLayerIdsToRemove.push(mbLayer.id);
- }
- });
- mbLayerIdsToRemove.forEach(mbLayerId => mbMap.removeLayer(mbLayerId));
-
- const mbSourcesToRemove = [];
- for (const mbSourceId in mbStyle.sources) {
- if (mbStyle.sources.hasOwnProperty(mbSourceId)) {
- const layer = layerList.find(layer => {
- return layer.ownsMbSourceId(mbSourceId);
- });
- if (!layer) {
- mbSourcesToRemove.push(mbSourceId);
- }
- }
- }
- mbSourcesToRemove.forEach(mbSourceId => mbMap.removeSource(mbSourceId));
-}
-
-/**
- * This is function assumes only a single layer moved in the layerList, compared to mbMap
- * It is optimized to minimize the amount of mbMap.moveLayer calls.
- * @param mbMap
- * @param layerList
- */
-export function syncLayerOrderForSingleLayer(mbMap, layerList) {
- if (!layerList || layerList.length === 0) {
- return;
- }
-
- const mbLayers = mbMap.getStyle().layers.slice();
- const layerIds = mbLayers.map(mbLayer => {
- const layer = layerList.find(layer => layer.ownsMbLayerId(mbLayer.id));
- return layer.getId();
- });
-
- const currentLayerOrderLayerIds = _.uniq(layerIds);
-
- const newLayerOrderLayerIdsUnfiltered = layerList.map(l => l.getId());
- const newLayerOrderLayerIds = newLayerOrderLayerIdsUnfiltered.filter(layerId =>
- currentLayerOrderLayerIds.includes(layerId)
- );
-
- let netPos = 0;
- let netNeg = 0;
- const movementArr = currentLayerOrderLayerIds.reduce((accu, id, idx) => {
- const movement = newLayerOrderLayerIds.findIndex(newOId => newOId === id) - idx;
- movement > 0 ? netPos++ : movement < 0 && netNeg++;
- accu.push({ id, movement });
- return accu;
- }, []);
- if (netPos === 0 && netNeg === 0) {
- return;
- }
- const movedLayerId =
- (netPos >= netNeg && movementArr.find(l => l.movement < 0).id) ||
- (netPos < netNeg && movementArr.find(l => l.movement > 0).id);
- const nextLayerIdx = newLayerOrderLayerIds.findIndex(layerId => layerId === movedLayerId) + 1;
-
- let nextMbLayerId;
- if (nextLayerIdx === newLayerOrderLayerIds.length) {
- nextMbLayerId = null;
- } else {
- const foundLayer = mbLayers.find(({ id: mbLayerId }) => {
- const layerId = newLayerOrderLayerIds[nextLayerIdx];
- const layer = layerList.find(layer => layer.getId() === layerId);
- return layer.ownsMbLayerId(mbLayerId);
- });
- nextMbLayerId = foundLayer.id;
- }
-
- const movedLayer = layerList.find(layer => layer.getId() === movedLayerId);
- mbLayers.forEach(({ id: mbLayerId }) => {
- if (movedLayer.ownsMbLayerId(mbLayerId)) {
- mbMap.moveLayer(mbLayerId, nextMbLayerId);
- }
- });
-}
-
-export async function addSpritesheetToMap(json, imgUrl, mbMap) {
- const imgData = await loadSpriteSheetImageData(imgUrl);
- addSpriteSheetToMapFromImageData(json, imgData, mbMap);
-}
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_index.scss
deleted file mode 100644
index cc1ab35039dac..0000000000000
--- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/_index.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-@import './mixins';
-
-@import './widget_overlay';
-@import './attribution_control/attribution_control';
-@import './layer_control/index';
-@import './view_control/view_control';
diff --git a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss b/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss
deleted file mode 100644
index 761ef9d17b4c2..0000000000000
--- a/x-pack/legacy/plugins/maps/public/connected_components/widget_overlay/layer_control/_index.scss
+++ /dev/null
@@ -1,2 +0,0 @@
-@import './layer_control';
-@import './layer_toc/toc_entry/toc_entry';
diff --git a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts
index 96c3baf634a83..90b17412377f5 100644
--- a/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts
+++ b/x-pack/legacy/plugins/maps/public/embeddable/map_embeddable_factory.ts
@@ -4,153 +4,25 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import _ from 'lodash';
-import { i18n } from '@kbn/i18n';
+/*
+ Maintain legacy embeddable legacy present while apps switch over
+ */
+
import { npSetup, npStart } from 'ui/new_platform';
-import { IIndexPattern } from 'src/plugins/data/public';
-// @ts-ignore
-import { getMapsSavedObjectLoader } from '../angular/services/gis_map_saved_object_loader';
-import { MapEmbeddable, MapEmbeddableInput } from './map_embeddable';
import {
- getIndexPatternService,
- getHttp,
- getMapsCapabilities,
+ bindSetupCoreAndPlugins,
+ bindStartCoreAndPlugins,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-} from '../../../../../plugins/maps/public/kibana_services';
-import {
- EmbeddableFactoryDefinition,
- IContainer,
-} from '../../../../../../src/plugins/embeddable/public';
-
-import { createMapPath, MAP_SAVED_OBJECT_TYPE, APP_ICON } from '../../common/constants';
+} from '../../../../../plugins/maps/public/plugin';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { createMapStore } from '../../../../../plugins/maps/public/reducers/store';
-import { addLayerWithoutDataSync } from '../actions/map_actions';
-import { getQueryableUniqueIndexPatternIds } from '../selectors/map_selectors';
-import { getInitialLayers } from '../angular/get_initial_layers';
-import { mergeInputWithSavedMap } from './merge_input_with_saved_map';
-import '../angular/services/gis_map_saved_object_loader';
-// @ts-ignore
-import {
- bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins,
- bindStartCoreAndPlugins as bindNpStartCoreAndPlugins,
-} from '../../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths
-
-export class MapEmbeddableFactory implements EmbeddableFactoryDefinition {
- type = MAP_SAVED_OBJECT_TYPE;
- savedObjectMetaData = {
- name: i18n.translate('xpack.maps.mapSavedObjectLabel', {
- defaultMessage: 'Map',
- }),
- type: MAP_SAVED_OBJECT_TYPE,
- getIconForSavedObject: () => APP_ICON,
- };
- constructor() {
- // Init required services. Necessary while in legacy
- bindNpSetupCoreAndPlugins(npSetup.core, npSetup.plugins);
- bindNpStartCoreAndPlugins(npStart.core, npStart.plugins);
- }
-
- async isEditable() {
- return getMapsCapabilities().save as boolean;
- }
-
- // Not supported yet for maps types.
- canCreateNew() {
- return false;
- }
-
- getDisplayName() {
- return i18n.translate('xpack.maps.embeddableDisplayName', {
- defaultMessage: 'map',
- });
- }
-
- async _getIndexPatterns(layerList: unknown[]): Promise {
- // Need to extract layerList from store to get queryable index pattern ids
- const store = createMapStore();
- let queryableIndexPatternIds;
- try {
- layerList.forEach((layerDescriptor: unknown) => {
- store.dispatch(addLayerWithoutDataSync(layerDescriptor));
- });
- queryableIndexPatternIds = getQueryableUniqueIndexPatternIds(store.getState());
- } catch (error) {
- throw new Error(
- i18n.translate('xpack.maps.mapEmbeddableFactory.invalidLayerList', {
- defaultMessage: 'Unable to load map, malformed layer list',
- })
- );
- }
-
- const promises = queryableIndexPatternIds.map(async indexPatternId => {
- try {
- return await getIndexPatternService().get(indexPatternId);
- } catch (error) {
- // Unable to load index pattern, better to not throw error so map embeddable can render
- // Error will be surfaced by map embeddable since it too will be unable to locate the index pattern
- return null;
- }
- });
- const indexPatterns = await Promise.all(promises);
- return _.compact(indexPatterns) as IIndexPattern[];
- }
-
- async _fetchSavedMap(savedObjectId: string) {
- const savedObjectLoader = getMapsSavedObjectLoader();
- return await savedObjectLoader.get(savedObjectId);
- }
-
- createFromSavedObject = async (
- savedObjectId: string,
- input: MapEmbeddableInput,
- parent?: IContainer
- ) => {
- const savedMap = await this._fetchSavedMap(savedObjectId);
- const layerList = getInitialLayers(savedMap.layerListJSON);
- const indexPatterns = await this._getIndexPatterns(layerList);
-
- const embeddable = new MapEmbeddable(
- {
- layerList,
- title: savedMap.title,
- editUrl: getHttp().basePath.prepend(createMapPath(savedObjectId)),
- indexPatterns,
- editable: await this.isEditable(),
- },
- input,
- parent
- );
-
- try {
- embeddable.updateInput(mergeInputWithSavedMap(input, savedMap));
- } catch (error) {
- throw new Error(
- i18n.translate('xpack.maps.mapEmbeddableFactory.invalidSavedObject', {
- defaultMessage: 'Unable to load map, malformed saved object',
- })
- );
- }
-
- return embeddable;
- };
+import { MAP_SAVED_OBJECT_TYPE } from '../../../../../plugins/maps/common/constants';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { MapEmbeddableFactory } from '../../../../../plugins/maps/public/embeddable';
- create = async (input: MapEmbeddableInput, parent?: IContainer) => {
- const layerList = getInitialLayers();
- const indexPatterns = await this._getIndexPatterns(layerList);
+bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins);
+bindStartCoreAndPlugins(npStart.core, npStart.plugins);
- return new MapEmbeddable(
- {
- layerList,
- title: input.title ?? '',
- indexPatterns,
- editable: false,
- },
- input,
- parent
- );
- };
-}
+export * from '../../../../../plugins/maps/public/embeddable/map_embeddable_factory';
npSetup.plugins.embeddable.registerEmbeddableFactory(
MAP_SAVED_OBJECT_TYPE,
diff --git a/x-pack/legacy/plugins/maps/public/index.scss b/x-pack/legacy/plugins/maps/public/index.scss
index b2ac514299d80..b2a228f01b921 100644
--- a/x-pack/legacy/plugins/maps/public/index.scss
+++ b/x-pack/legacy/plugins/maps/public/index.scss
@@ -1,17 +1,3 @@
-// Import the EUI global scope so we can use EUI constants
-@import 'src/legacy/ui/public/styles/_styling_constants';
-
/* GIS plugin styles */
-// Prefix all styles with "map" to avoid conflicts.
-// Examples
-// mapChart
-// mapChart__legend
-// mapChart__legend--small
-// mapChart__legend-isLoading
-
-@import './main';
-@import './mapbox_hacks';
-@import './connected_components/index';
-@import './components/index';
-@import '../../../../plugins/maps/public/layers/index';
+@import '../../../../plugins/maps/public/index';
diff --git a/x-pack/legacy/plugins/maps/public/index.ts b/x-pack/legacy/plugins/maps/public/index.ts
index 8555594e909d3..98db26859297b 100644
--- a/x-pack/legacy/plugins/maps/public/index.ts
+++ b/x-pack/legacy/plugins/maps/public/index.ts
@@ -15,15 +15,14 @@ import 'uiExports/embeddableActions';
import 'ui/autoload/all';
import 'react-vis/dist/style.css';
-
-import './angular/services/gis_map_saved_object_loader';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import '../../../../plugins/maps/public/angular/services/gis_map_saved_object_loader';
import './angular/map_controller';
import './routes';
// @ts-ignore
-import { PluginInitializerContext } from 'kibana/public';
import { MapsPlugin } from './plugin';
-export const plugin = (initializerContext: PluginInitializerContext) => {
+export const plugin = () => {
return new MapsPlugin();
};
@@ -32,4 +31,4 @@ export {
RenderTooltipContentParams,
ITooltipProperty,
} from '../../../../plugins/maps/public/layers/tooltips/tooltip_property';
-export { MapEmbeddable, MapEmbeddableInput } from './embeddable';
+export { MapEmbeddable, MapEmbeddableInput } from '../../../../plugins/maps/public/embeddable';
diff --git a/x-pack/legacy/plugins/maps/public/legacy.ts b/x-pack/legacy/plugins/maps/public/legacy.ts
index 96d9e09c1d09a..bcbfca17755fb 100644
--- a/x-pack/legacy/plugins/maps/public/legacy.ts
+++ b/x-pack/legacy/plugins/maps/public/legacy.ts
@@ -7,10 +7,9 @@
import { npSetup, npStart } from 'ui/new_platform';
// @ts-ignore Untyped Module
import { uiModules } from 'ui/modules';
-import { PluginInitializerContext } from 'kibana/public'; // eslint-disable-line import/order
import { plugin } from '.';
-const pluginInstance = plugin({} as PluginInitializerContext);
+const pluginInstance = plugin();
const setupPlugins = {
__LEGACY: {
diff --git a/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts b/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts
deleted file mode 100644
index 00f788f267d4b..0000000000000
--- a/x-pack/legacy/plugins/maps/public/legacy_register_feature.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { npSetup } from 'ui/new_platform';
-import { featureCatalogueEntry } from './feature_catalogue_entry';
-
-const {
- plugins: { home },
-} = npSetup;
-
-home.featureCatalogue.register(featureCatalogueEntry);
diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts
index 4ec068ff44029..0123e32b6d3b9 100644
--- a/x-pack/legacy/plugins/maps/public/plugin.ts
+++ b/x-pack/legacy/plugins/maps/public/plugin.ts
@@ -4,20 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../plugins/maps/public/layers/layer_wizard_registry';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../plugins/maps/public/layers/sources/source_registry';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import '../../../../plugins/maps/public/layers/load_layer_wizards';
-
import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
// @ts-ignore
import { Start as InspectorStartContract } from 'src/plugins/inspector/public';
// @ts-ignore
import { wrapInI18nContext } from 'ui/i18n';
// @ts-ignore
-import { MapListing } from './components/map_listing';
+import { MapListing } from '../../../../plugins/maps/public/components/map_listing'; // eslint-disable-line @kbn/eslint/no-restricted-paths
// @ts-ignore
import {
bindSetupCoreAndPlugins as bindNpSetupCoreAndPlugins,
@@ -25,7 +18,6 @@ import {
} from '../../../../plugins/maps/public/plugin'; // eslint-disable-line @kbn/eslint/no-restricted-paths
import { HomePublicPluginSetup } from '../../../../../src/plugins/home/public';
import { LicensingPluginSetup } from '../../../../plugins/licensing/public';
-import { featureCatalogueEntry } from './feature_catalogue_entry';
import {
DataPublicPluginSetup,
DataPublicPluginStart,
@@ -64,8 +56,6 @@ export class MapsPlugin implements Plugin {
});
bindNpSetupCoreAndPlugins(core, np);
-
- np.home.featureCatalogue.register(featureCatalogueEntry);
}
public start(core: CoreStart, plugins: MapsPluginStartDependencies) {
diff --git a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js b/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js
deleted file mode 100644
index 9dc07bcb5dc0e..0000000000000
--- a/x-pack/legacy/plugins/maps/public/register_vis_type_alias.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { i18n } from '@kbn/i18n';
-import { APP_ID, APP_ICON, MAP_BASE_URL } from '../common/constants';
-import {
- getInjectedVarFunc,
- getVisualizations,
- // eslint-disable-next-line @kbn/eslint/no-restricted-paths
-} from '../../../../plugins/maps/public/kibana_services';
-import { npSetup } from 'ui/new_platform';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { bindSetupCoreAndPlugins } from '../../../../plugins/maps/public/plugin';
-
-bindSetupCoreAndPlugins(npSetup.core, npSetup.plugins);
-
-const showMapVisualizationTypes = getInjectedVarFunc()('showMapVisualizationTypes', false);
-
-const description = i18n.translate('xpack.maps.visTypeAlias.description', {
- defaultMessage: 'Create and style maps with multiple layers and indices.',
-});
-
-const legacyMapVisualizationWarning = i18n.translate(
- 'xpack.maps.visTypeAlias.legacyMapVizWarning',
- {
- defaultMessage: `Use the Maps app instead of Coordinate Map and Region Map.
-The Maps app offers more functionality and is easier to use.`,
- }
-);
-
-getVisualizations().registerAlias({
- aliasUrl: MAP_BASE_URL,
- name: APP_ID,
- title: i18n.translate('xpack.maps.visTypeAlias.title', {
- defaultMessage: 'Maps',
- }),
- description: showMapVisualizationTypes
- ? `${description} ${legacyMapVisualizationWarning}`
- : description,
- icon: APP_ICON,
- stage: 'production',
-});
-
-if (!showMapVisualizationTypes) {
- getVisualizations().hideTypes(['region_map', 'tile_map']);
-}
diff --git a/x-pack/legacy/plugins/maps/public/routes.js b/x-pack/legacy/plugins/maps/public/routes.js
index c082e0e1352c0..70c1c4a50efd4 100644
--- a/x-pack/legacy/plugins/maps/public/routes.js
+++ b/x-pack/legacy/plugins/maps/public/routes.js
@@ -6,15 +6,18 @@
import { i18n } from '@kbn/i18n';
import routes from 'ui/routes';
-import listingTemplate from './angular/listing_ng_wrapper.html';
-import mapTemplate from './angular/map.html';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import listingTemplate from '../../../../plugins/maps/public/angular/listing_ng_wrapper.html';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import mapTemplate from '../../../../plugins/maps/public/angular/map.html';
import {
getSavedObjectsClient,
getCoreChrome,
getMapsCapabilities,
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
} from '../../../../plugins/maps/public/kibana_services';
-import { getMapsSavedObjectLoader } from './angular/services/gis_map_saved_object_loader';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getMapsSavedObjectLoader } from '../../../../plugins/maps/public/angular/services/gis_map_saved_object_loader';
routes.enable();
diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js
index c5522b7ba21c5..e2a758075155a 100644
--- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js
+++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.js
@@ -5,7 +5,10 @@
*/
import _ from 'lodash';
-import { DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_INNER_RESULT_WINDOW } from '../../common/constants';
+import {
+ DEFAULT_MAX_RESULT_WINDOW,
+ DEFAULT_MAX_INNER_RESULT_WINDOW,
+} from '../../../../../plugins/maps/common/constants';
export function getIndexPatternSettings(indicesSettingsResp) {
let maxResultWindow = Infinity;
diff --git a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js
index 01a1ba2703cba..c152f5bfffc31 100644
--- a/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js
+++ b/x-pack/legacy/plugins/maps/server/lib/get_index_pattern_settings.test.js
@@ -5,7 +5,10 @@
*/
import { getIndexPatternSettings } from './get_index_pattern_settings';
-import { DEFAULT_MAX_RESULT_WINDOW, DEFAULT_MAX_INNER_RESULT_WINDOW } from '../../common/constants';
+import {
+ DEFAULT_MAX_RESULT_WINDOW,
+ DEFAULT_MAX_INNER_RESULT_WINDOW,
+} from '../../../../../plugins/maps/common/constants';
describe('max_result_window and max_inner_result_window are not set', () => {
test('Should provide default values when values not set', () => {
diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts
index 652bb83a0d781..d34e306d1fff9 100644
--- a/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts
+++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/collectors/register.ts
@@ -9,7 +9,7 @@ import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { SavedObjectsClientContract } from 'src/core/server';
import { getMapsTelemetry } from '../maps_telemetry';
// @ts-ignore
-import { TELEMETRY_TYPE } from '../../../common/constants';
+import { TELEMETRY_TYPE } from '../../../../../../plugins/maps/common/constants';
export function registerMapsUsageCollector(
usageCollection: UsageCollectionSetup,
diff --git a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
index 27c0211446e85..4610baabad3fe 100644
--- a/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
+++ b/x-pack/legacy/plugins/maps/server/maps_telemetry/maps_telemetry.ts
@@ -16,8 +16,8 @@ import {
ES_GEO_FIELD_TYPE,
MAP_SAVED_OBJECT_TYPE,
TELEMETRY_TYPE,
-} from '../../common/constants';
-import { LayerDescriptor } from '../../common/descriptor_types';
+} from '../../../../../plugins/maps/common/constants';
+import { LayerDescriptor } from '../../../../../plugins/maps/common/descriptor_types';
import { MapSavedObject } from '../../../../../plugins/maps/common/map_saved_object_type';
interface IStats {
diff --git a/x-pack/legacy/plugins/maps/server/plugin.js b/x-pack/legacy/plugins/maps/server/plugin.js
index 25c552433e9f8..79f3dcf76b82e 100644
--- a/x-pack/legacy/plugins/maps/server/plugin.js
+++ b/x-pack/legacy/plugins/maps/server/plugin.js
@@ -4,7 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
-import { APP_ID, APP_ICON, createMapPath, MAP_SAVED_OBJECT_TYPE } from '../common/constants';
+import {
+ APP_ID,
+ APP_ICON,
+ createMapPath,
+ MAP_SAVED_OBJECT_TYPE,
+} from '../../../../plugins/maps/common/constants';
import { getEcommerceSavedObjects } from './sample_data/ecommerce_saved_objects';
import { getFlightsSavedObjects } from './sample_data/flights_saved_objects.js';
import { getWebLogsSavedObjects } from './sample_data/web_logs_saved_objects.js';
diff --git a/x-pack/legacy/plugins/maps/server/routes.js b/x-pack/legacy/plugins/maps/server/routes.js
index 20e022001577a..d49f9827e3ea0 100644
--- a/x-pack/legacy/plugins/maps/server/routes.js
+++ b/x-pack/legacy/plugins/maps/server/routes.js
@@ -21,7 +21,7 @@ import {
GIS_API_PATH,
EMS_SPRITES_PATH,
INDEX_SETTINGS_API_PATH,
-} from '../common/constants';
+} from '../../../../plugins/maps/common/constants';
import { EMSClient } from '@elastic/ems-client';
import fetch from 'node-fetch';
import { i18n } from '@kbn/i18n';
diff --git a/x-pack/legacy/plugins/reporting/common/constants.ts b/x-pack/legacy/plugins/reporting/common/constants.ts
index e3d6a4274e7df..f30a7cc87f318 100644
--- a/x-pack/legacy/plugins/reporting/common/constants.ts
+++ b/x-pack/legacy/plugins/reporting/common/constants.ts
@@ -20,6 +20,7 @@ export const API_GENERATE_IMMEDIATE = `${API_BASE_URL_V1}/generate/immediate/csv
export const CONTENT_TYPE_CSV = 'text/csv';
export const CSV_REPORTING_ACTION = 'downloadCsvReport';
export const CSV_BOM_CHARS = '\ufeff';
+export const CSV_FORMULA_CHARS = ['=', '+', '-', '@'];
export const WHITELISTED_JOB_CONTENT_TYPES = [
'application/json',
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts
index f0afade8629ab..ad35aaf003094 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.test.ts
@@ -300,7 +300,7 @@ describe('CSV Execute Job', function() {
});
});
- describe('Cells with formula values', () => {
+ describe('Warning when cells have formulas', () => {
it('returns `csv_contains_formulas` when cells contain formulas', async function() {
configGetStub.withArgs('csv', 'checkForFormulas').returns(true);
callAsCurrentUserStub.onFirstCall().returns({
@@ -353,6 +353,7 @@ describe('CSV Execute Job', function() {
it('returns no warnings when cells have no formulas', async function() {
configGetStub.withArgs('csv', 'checkForFormulas').returns(true);
+ configGetStub.withArgs('csv', 'escapeFormulaValues').returns(false);
callAsCurrentUserStub.onFirstCall().returns({
hits: {
hits: [{ _source: { one: 'foo', two: 'bar' } }],
@@ -376,6 +377,33 @@ describe('CSV Execute Job', function() {
expect(csvContainsFormulas).toEqual(false);
});
+ it('returns no warnings when cells have formulas but are escaped', async function() {
+ configGetStub.withArgs('csv', 'checkForFormulas').returns(true);
+ configGetStub.withArgs('csv', 'escapeFormulaValues').returns(true);
+ callAsCurrentUserStub.onFirstCall().returns({
+ hits: {
+ hits: [{ _source: { '=SUM(A1:A2)': 'foo', two: 'bar' } }],
+ },
+ _scroll_id: 'scrollId',
+ });
+
+ const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger);
+ const jobParams = getJobDocPayload({
+ headers: encryptedHeaders,
+ fields: ['=SUM(A1:A2)', 'two'],
+ conflictedTypesFields: [],
+ searchRequest: { index: null, body: null },
+ });
+
+ const { csv_contains_formulas: csvContainsFormulas } = await executeJob(
+ 'job123',
+ jobParams,
+ cancellationToken
+ );
+
+ expect(csvContainsFormulas).toEqual(false);
+ });
+
it('returns no warnings when configured not to', async () => {
configGetStub.withArgs('csv', 'checkForFormulas').returns(false);
callAsCurrentUserStub.onFirstCall().returns({
@@ -446,6 +474,50 @@ describe('CSV Execute Job', function() {
});
});
+ describe('Escaping cells with formulas', () => {
+ it('escapes values with formulas', async () => {
+ configGetStub.withArgs('csv', 'escapeFormulaValues').returns(true);
+ callAsCurrentUserStub.onFirstCall().returns({
+ hits: {
+ hits: [{ _source: { one: `=cmd|' /C calc'!A0`, two: 'bar' } }],
+ },
+ _scroll_id: 'scrollId',
+ });
+
+ const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger);
+ const jobParams = getJobDocPayload({
+ headers: encryptedHeaders,
+ fields: ['one', 'two'],
+ conflictedTypesFields: [],
+ searchRequest: { index: null, body: null },
+ });
+ const { content } = await executeJob('job123', jobParams, cancellationToken);
+
+ expect(content).toEqual("one,two\n\"'=cmd|' /C calc'!A0\",bar\n");
+ });
+
+ it('does not escapes values with formulas', async () => {
+ configGetStub.withArgs('csv', 'escapeFormulaValues').returns(false);
+ callAsCurrentUserStub.onFirstCall().returns({
+ hits: {
+ hits: [{ _source: { one: `=cmd|' /C calc'!A0`, two: 'bar' } }],
+ },
+ _scroll_id: 'scrollId',
+ });
+
+ const executeJob = await executeJobFactory(mockReportingPlugin, mockLogger);
+ const jobParams = getJobDocPayload({
+ headers: encryptedHeaders,
+ fields: ['one', 'two'],
+ conflictedTypesFields: [],
+ searchRequest: { index: null, body: null },
+ });
+ const { content } = await executeJob('job123', jobParams, cancellationToken);
+
+ expect(content).toEqual('one,two\n"=cmd|\' /C calc\'!A0",bar\n');
+ });
+ });
+
describe('Elasticsearch call errors', function() {
it('should reject Promise if search call errors out', async function() {
callAsCurrentUserStub.rejects(new Error());
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts
index 376a398da274f..dbe305bc452db 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/execute_job.ts
@@ -123,7 +123,7 @@ export const executeJobFactory: ExecuteJobFactory
+ CSV_FORMULA_CHARS.some(formulaChar => startsWith(val, formulaChar));
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts
index 09f7cd2061ffb..0ec39c527d656 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/check_cells_for_formulas.ts
@@ -5,8 +5,7 @@
*/
import * as _ from 'lodash';
-
-const formulaValues = ['=', '+', '-', '@'];
+import { cellHasFormulas } from './cell_has_formula';
interface IFlattened {
[header: string]: string;
@@ -14,7 +13,7 @@ interface IFlattened {
export const checkIfRowsHaveFormulas = (flattened: IFlattened, fields: string[]) => {
const pruned = _.pick(flattened, fields);
- const csvValues = [..._.keys(pruned), ...(_.values(pruned) as string[])];
+ const cells = [..._.keys(pruned), ...(_.values(pruned) as string[])];
- return _.some(csvValues, cell => _.some(formulaValues, char => _.startsWith(cell, char)));
+ return _.some(cells, cell => cellHasFormulas(cell));
};
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts
index 64b021a2aeea8..dd0f9d08b864b 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.test.ts
@@ -11,7 +11,7 @@ describe('escapeValue', function() {
describe('quoteValues is true', function() {
let escapeValue: (val: string) => string;
beforeEach(function() {
- escapeValue = createEscapeValue(true);
+ escapeValue = createEscapeValue(true, false);
});
it('should escape value with spaces', function() {
@@ -46,7 +46,7 @@ describe('escapeValue', function() {
describe('quoteValues is false', function() {
let escapeValue: (val: string) => string;
beforeEach(function() {
- escapeValue = createEscapeValue(false);
+ escapeValue = createEscapeValue(false, false);
});
it('should return the value unescaped', function() {
@@ -54,4 +54,34 @@ describe('escapeValue', function() {
expect(escapeValue(value)).to.be(value);
});
});
+
+ describe('escapeValues', () => {
+ describe('when true', () => {
+ let escapeValue: (val: string) => string;
+ beforeEach(function() {
+ escapeValue = createEscapeValue(true, true);
+ });
+
+ ['@', '+', '-', '='].forEach(badChar => {
+ it(`should escape ${badChar} injection values`, function() {
+ expect(escapeValue(`${badChar}cmd|' /C calc'!A0`)).to.be(
+ `"'${badChar}cmd|' /C calc'!A0"`
+ );
+ });
+ });
+ });
+
+ describe('when false', () => {
+ let escapeValue: (val: string) => string;
+ beforeEach(function() {
+ escapeValue = createEscapeValue(true, false);
+ });
+
+ ['@', '+', '-', '='].forEach(badChar => {
+ it(`should not escape ${badChar} injection values`, function() {
+ expect(escapeValue(`${badChar}cmd|' /C calc'!A0`)).to.be(`"${badChar}cmd|' /C calc'!A0"`);
+ });
+ });
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts
index 563de563350e9..60e75d74b2f98 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/escape_value.ts
@@ -5,15 +5,20 @@
*/
import { RawValue } from './types';
+import { cellHasFormulas } from './cell_has_formula';
const nonAlphaNumRE = /[^a-zA-Z0-9]/;
const allDoubleQuoteRE = /"/g;
-export function createEscapeValue(quoteValues: boolean): (val: RawValue) => string {
+export function createEscapeValue(
+ quoteValues: boolean,
+ escapeFormulas: boolean
+): (val: RawValue) => string {
return function escapeValue(val: RawValue) {
if (val && typeof val === 'string') {
- if (quoteValues && nonAlphaNumRE.test(val)) {
- return `"${val.replace(allDoubleQuoteRE, '""')}"`;
+ const formulasEscaped = escapeFormulas && cellHasFormulas(val) ? "'" + val : val;
+ if (quoteValues && nonAlphaNumRE.test(formulasEscaped)) {
+ return `"${formulasEscaped.replace(allDoubleQuoteRE, '""')}"`;
}
}
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts
index 1986e68917ba8..c7996ebf832a1 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv/server/lib/generate_csv.ts
@@ -4,6 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { i18n } from '@kbn/i18n';
import { Logger } from '../../../../types';
import { GenerateCsvParams, SavedSearchGeneratorResult } from '../../types';
import { createFlattenHit } from './flatten_hit';
@@ -26,14 +27,17 @@ export function createGenerateCsv(logger: Logger) {
cancellationToken,
settings,
}: GenerateCsvParams): Promise {
- const escapeValue = createEscapeValue(settings.quoteValues);
+ const escapeValue = createEscapeValue(settings.quoteValues, settings.escapeFormulaValues);
const builder = new MaxSizeStringBuilder(settings.maxSizeBytes);
const header = `${fields.map(escapeValue).join(settings.separator)}\n`;
+ const warnings: string[] = [];
+
if (!builder.tryAppend(header)) {
return {
size: 0,
content: '',
maxSizeReached: true,
+ warnings: [],
};
}
@@ -82,11 +86,20 @@ export function createGenerateCsv(logger: Logger) {
const size = builder.getSizeInBytes();
logger.debug(`finished generating, total size in bytes: ${size}`);
+ if (csvContainsFormulas && settings.escapeFormulaValues) {
+ warnings.push(
+ i18n.translate('xpack.reporting.exportTypes.csv.generateCsv.escapedFormulaValues', {
+ defaultMessage: 'CSV may contain formulas whose values have been escaped',
+ })
+ );
+ }
+
return {
content: builder.getString(),
- csvContainsFormulas,
+ csvContainsFormulas: csvContainsFormulas && !settings.escapeFormulaValues,
maxSizeReached,
size,
+ warnings,
};
};
}
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts b/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts
index 529c195486bc6..40a42db352635 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv/types.d.ts
@@ -87,6 +87,7 @@ export interface SavedSearchGeneratorResult {
size: number;
maxSizeReached: boolean;
csvContainsFormulas?: boolean;
+ warnings: string[];
}
export interface CsvResultFromSearch {
@@ -109,5 +110,6 @@ export interface GenerateCsvParams {
maxSizeBytes: number;
scroll: ScrollConfig;
checkForFormulas?: boolean;
+ escapeFormulaValues: boolean;
};
}
diff --git a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts
index 9757c71c19cf4..2611b74c83de9 100644
--- a/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts
+++ b/x-pack/legacy/plugins/reporting/export_types/csv_from_savedobject/server/lib/generate_csv_search.ts
@@ -173,6 +173,7 @@ export async function generateCsvSearch(
...uiSettings,
maxSizeBytes: config.get('csv', 'maxSizeBytes'),
scroll: config.get('csv', 'scroll'),
+ escapeFormulaValues: config.get('csv', 'escapeFormulaValues'),
timezone,
},
};
diff --git a/x-pack/legacy/plugins/rollup/README.md b/x-pack/legacy/plugins/rollup/README.md
index 6d04973de591e..3647be38b6a09 100644
--- a/x-pack/legacy/plugins/rollup/README.md
+++ b/x-pack/legacy/plugins/rollup/README.md
@@ -14,7 +14,7 @@ The rest of this doc dives into the implementation details of each of the above
## Create and manage rollup jobs
-The most straight forward part of this plugin! A new app called Rollup Jobs is registered in the Management section and follows a typical CRUD UI pattern. This app allows users to create, start, stop, clone, and delete rollup jobs. There is no way to edit an existing rollup job; instead, the UI offers a cloning ability. The client-side portion of this app lives [here](public/crud_app) and uses endpoints registered [here](server/routes/api/jobs.js).
+The most straight forward part of this plugin! A new app called Rollup Jobs is registered in the Management section and follows a typical CRUD UI pattern. This app allows users to create, start, stop, clone, and delete rollup jobs. There is no way to edit an existing rollup job; instead, the UI offers a cloning ability. The client-side portion of this app lives [here](../../../plugins/rollup/public/crud_app) and uses endpoints registered [here](server/routes/api/jobs.js).
Refer to the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-getting-started.html) to understand rollup indices and how to create rollup jobs.
@@ -22,22 +22,22 @@ Refer to the [Elasticsearch documentation](https://www.elastic.co/guide/en/elast
Kibana uses index patterns to consume and visualize rollup indices. Typically, Kibana can inspect the indices captured by an index pattern, identify its aggregations and fields, and determine how to consume the data. Rollup indices don't contain this type of information, so we predefine how to consume a rollup index pattern with the type and typeMeta fields on the index pattern saved object. All rollup index patterns have `type` defined as "rollup" and `typeMeta` defined as an object of the index pattern's capabilities.
-In the Index Pattern app, the "Create index pattern" button includes a context menu when a rollup index is detected. This menu offers items for creating a standard index pattern and a rollup index pattern. A [rollup config is registered to index pattern creation extension point](public/index_pattern_creation/rollup_index_pattern_creation_config.js). The context menu behavior in particular uses the `getIndexPatternCreationOption()` method. When the user chooses to create a rollup index pattern, this config changes the behavior of the index pattern creation wizard:
+In the Index Pattern app, the "Create index pattern" button includes a context menu when a rollup index is detected. This menu offers items for creating a standard index pattern and a rollup index pattern. A [rollup config is registered to index pattern creation extension point](../../../plugins/rollup/public/index_pattern_creation/rollup_index_pattern_creation_config.js). The context menu behavior in particular uses the `getIndexPatternCreationOption()` method. When the user chooses to create a rollup index pattern, this config changes the behavior of the index pattern creation wizard:
1. Adds a `Rollup` badge to rollup indices using `getIndexTags()`.
2. Enforces index pattern rules using `checkIndicesForErrors()`. Rollup index patterns must match **one** rollup index, and optionally, any number of regular indices. A rollup index pattern configured with one or more regular indices is known as a "hybrid" index pattern. This allows the user to visualize historical (rollup) data and live (regular) data in the same visualization.
3. Routes to this plugin's [rollup `_fields_for_wildcard` endpoint](server/routes/api/index_patterns.js), instead of the standard one, using `getFetchForWildcardOptions()`, so that the internal rollup data field names are mapped to the original field names.
4. Writes additional information about aggregations, fields, histogram interval, and date histogram interval and timezone to the rollup index pattern saved object using `getIndexPatternMappings()`. This collection of information is referred to as its "capabilities".
-Once a rollup index pattern is created, it is tagged with `Rollup` in the list of index patterns, and its details page displays capabilities information. This is done by registering [yet another config for the index pattern list](public/index_pattern_list/rollup_index_pattern_list_config.js) extension points.
+Once a rollup index pattern is created, it is tagged with `Rollup` in the list of index patterns, and its details page displays capabilities information. This is done by registering [yet another config for the index pattern list](../../../plugins/rollup/public/index_pattern_list/rollup_index_pattern_list_config.js) extension points.
## Create visualizations from rollup index patterns
This plugin enables the user to create visualizations from rollup data using the Visualize app, excluding TSVB, Vega, and Timelion. When Visualize sends search requests, this plugin routes the requests to the [Elasticsearch rollup search endpoint](https://www.elastic.co/guide/en/elasticsearch/reference/current/rollup-search.html), which searches the special document structure within rollup indices. The visualization options available to users are based on the capabilities of the rollup index pattern they're visualizing.
-Routing to the Elasticsearch rollup search endpoint is done by creating an extension point in Courier, effectively allowing multiple "search strategies" to be registered. A [rollup search strategy](public/search/register.js) is registered by this plugin that queries [this plugin's rollup search endpoint](server/routes/api/search.js).
+Routing to the Elasticsearch rollup search endpoint is done by creating an extension point in Courier, effectively allowing multiple "search strategies" to be registered. A [rollup search strategy](../../../plugins/rollup/public/search/register.js) is registered by this plugin that queries [this plugin's rollup search endpoint](server/routes/api/search.js).
-Limiting visualization editor options is done by [registering configs](public/visualize/index.js) to various vis extension points. These configs use information stored on the rollup index pattern to limit:
+Limiting visualization editor options is done by [registering configs](../../../plugins/rollup/public/visualize/index.js) to various vis extension points. These configs use information stored on the rollup index pattern to limit:
* Available aggregation types
* Available fields for a particular aggregation
* Default and base interval for histogram aggregation
@@ -47,6 +47,6 @@ Limiting visualization editor options is done by [registering configs](public/vi
In Index Management, similar to system indices, rollup indices are hidden by default. A toggle is provided to show rollup indices and add a badge to the table rows. This is done by using Index Management's extension points.
-The toggle and badge are registered on client-side [here](public/extend_index_management/index.js).
+The toggle and badge are registered on client-side [here](../../../plugins/rollup/public/extend_index_management/index.js).
Additional data needed to filter rollup indices in Index Management is provided with a [data enricher](rollup_data_enricher.js).
diff --git a/x-pack/legacy/plugins/rollup/common/index.ts b/x-pack/legacy/plugins/rollup/common/index.ts
index 4229803462203..526af055a3ef6 100644
--- a/x-pack/legacy/plugins/rollup/common/index.ts
+++ b/x-pack/legacy/plugins/rollup/common/index.ts
@@ -16,24 +16,4 @@ export const PLUGIN = {
},
};
-export const CONFIG_ROLLUPS = 'rollups:enableIndexPatterns';
-
-export const API_BASE_PATH = '/api/rollup';
-
-export {
- UIM_APP_NAME,
- UIM_APP_LOAD,
- UIM_JOB_CREATE,
- UIM_JOB_DELETE,
- UIM_JOB_DELETE_MANY,
- UIM_JOB_START,
- UIM_JOB_START_MANY,
- UIM_JOB_STOP,
- UIM_JOB_STOP_MANY,
- UIM_SHOW_DETAILS_CLICK,
- UIM_DETAIL_PANEL_SUMMARY_TAB_CLICK,
- UIM_DETAIL_PANEL_TERMS_TAB_CLICK,
- UIM_DETAIL_PANEL_HISTOGRAM_TAB_CLICK,
- UIM_DETAIL_PANEL_METRICS_TAB_CLICK,
- UIM_DETAIL_PANEL_JSON_TAB_CLICK,
-} from './ui_metric';
+export * from '../../../../plugins/rollup/common';
diff --git a/x-pack/legacy/plugins/rollup/index.ts b/x-pack/legacy/plugins/rollup/index.ts
index 621667f3618b2..f33ae7cfee0a2 100644
--- a/x-pack/legacy/plugins/rollup/index.ts
+++ b/x-pack/legacy/plugins/rollup/index.ts
@@ -4,40 +4,16 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { resolve } from 'path';
-import { i18n } from '@kbn/i18n';
import { PluginInitializerContext } from 'src/core/server';
import { RollupSetup } from '../../../plugins/rollup/server';
-import { PLUGIN, CONFIG_ROLLUPS } from './common';
+import { PLUGIN } from './common';
import { plugin } from './server';
export function rollup(kibana: any) {
return new kibana.Plugin({
id: PLUGIN.ID,
configPrefix: 'xpack.rollup',
- publicDir: resolve(__dirname, 'public'),
require: ['kibana', 'elasticsearch', 'xpack_main'],
- uiExports: {
- styleSheetPaths: resolve(__dirname, 'public/index.scss'),
- managementSections: ['plugins/rollup/legacy'],
- uiSettingDefaults: {
- [CONFIG_ROLLUPS]: {
- name: i18n.translate('xpack.rollupJobs.rollupIndexPatternsTitle', {
- defaultMessage: 'Enable rollup index patterns',
- }),
- value: true,
- description: i18n.translate('xpack.rollupJobs.rollupIndexPatternsDescription', {
- defaultMessage: `Enable the creation of index patterns which capture rollup indices,
- which in turn enable visualizations based on rollup data. Refresh
- the page to apply the changes.`,
- }),
- category: ['rollups'],
- },
- },
- indexManagement: ['plugins/rollup/legacy'],
- visualize: ['plugins/rollup/legacy'],
- search: ['plugins/rollup/legacy'],
- },
init(server: any) {
const { core: coreSetup, plugins } = server.newPlatform.setup;
const { usageCollection, visTypeTimeseries, indexManagement } = plugins;
diff --git a/x-pack/legacy/plugins/rollup/public/legacy.ts b/x-pack/legacy/plugins/rollup/public/legacy.ts
deleted file mode 100644
index 83945110c2c76..0000000000000
--- a/x-pack/legacy/plugins/rollup/public/legacy.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { npSetup, npStart } from 'ui/new_platform';
-import { RollupPlugin } from './plugin';
-
-const plugin = new RollupPlugin();
-
-export const setup = plugin.setup(npSetup.core, npSetup.plugins);
-export const start = plugin.start(npStart.core, npStart.plugins);
diff --git a/x-pack/legacy/plugins/rollup/public/legacy_imports.ts b/x-pack/legacy/plugins/rollup/public/legacy_imports.ts
deleted file mode 100644
index e82a41f60b1ca..0000000000000
--- a/x-pack/legacy/plugins/rollup/public/legacy_imports.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-export { PluginsStart } from 'ui/new_platform/new_platform';
diff --git a/x-pack/legacy/plugins/rollup/public/search/rollup_search_strategy.ts b/x-pack/legacy/plugins/rollup/public/search/rollup_search_strategy.ts
deleted file mode 100644
index 4709c0aa498f8..0000000000000
--- a/x-pack/legacy/plugins/rollup/public/search/rollup_search_strategy.ts
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License;
- * you may not use this file except in compliance with the Elastic License.
- */
-
-import { HttpSetup } from 'src/core/public';
-import {
- SearchError,
- getSearchErrorType,
- IIndexPattern,
- SearchStrategyProvider,
- SearchResponse,
- SearchRequest,
-} from '../../../../../../src/plugins/data/public';
-
-function serializeFetchParams(searchRequests: SearchRequest[]) {
- return JSON.stringify(
- searchRequests.map(searchRequestWithFetchParams => {
- const indexPattern =
- searchRequestWithFetchParams.index.title || searchRequestWithFetchParams.index;
- const {
- body: { size, aggs, query: _query },
- } = searchRequestWithFetchParams;
-
- const query = {
- size,
- aggregations: aggs,
- query: _query,
- };
-
- return { index: indexPattern, query };
- })
- );
-}
-
-// Rollup search always returns 0 hits, but visualizations expect search responses
-// to return hits > 0, otherwise they do not render. We fake the number of hits here
-// by counting the number of aggregation buckets/values returned by rollup search.
-function shimHitsInFetchResponse(response: SearchResponse[]) {
- return response.map(result => {
- const buckets = result.aggregations
- ? Object.keys(result.aggregations).reduce((allBuckets, agg) => {
- return allBuckets.concat(
- result.aggregations[agg].buckets || [result.aggregations[agg].value] || []
- );
- }, [])
- : [];
- return buckets && buckets.length
- ? {
- ...result,
- hits: {
- ...result.hits,
- total: buckets.length,
- },
- }
- : result;
- });
-}
-
-export const getRollupSearchStrategy = (fetch: HttpSetup['fetch']): SearchStrategyProvider => ({
- id: 'rollup',
-
- search: ({ searchRequests }) => {
- // Serialize the fetch params into a format suitable for the body of an ES query.
- const serializedFetchParams = serializeFetchParams(searchRequests);
-
- const controller = new AbortController();
- const promise = fetch('../api/rollup/search', {
- signal: controller.signal,
- method: 'POST',
- body: serializedFetchParams,
- });
-
- return {
- searching: promise.then(shimHitsInFetchResponse).catch(error => {
- const {
- body: { statusCode, error: title, message },
- res: { url },
- } = error;
-
- // Format fetch error as a SearchError.
- const searchError = new SearchError({
- status: statusCode,
- title,
- message: `Rollup search error: ${message}`,
- path: url,
- type: getSearchErrorType({ message }) || '',
- });
-
- return Promise.reject(searchError);
- }),
- abort: () => controller.abort(),
- };
- },
-
- isViable: (indexPattern: IIndexPattern) => {
- if (!indexPattern) {
- return false;
- }
-
- return indexPattern.type === 'rollup';
- },
-});
diff --git a/x-pack/legacy/plugins/siem/.gitattributes b/x-pack/legacy/plugins/siem/.gitattributes
index f40e829b65453..a4071d39e63c0 100644
--- a/x-pack/legacy/plugins/siem/.gitattributes
+++ b/x-pack/legacy/plugins/siem/.gitattributes
@@ -1,6 +1,5 @@
# Auto-collapse generated files in GitHub
# https://help.github.com/en/articles/customizing-how-changed-files-appear-on-github
x-pack/legacy/plugins/siem/public/graphql/types.ts linguist-generated=true
-x-pack/legacy/plugins/siem/server/graphql/types.ts linguist-generated=true
x-pack/legacy/plugins/siem/public/graphql/introspection.json linguist-generated=true
diff --git a/x-pack/legacy/plugins/siem/cypress.json b/x-pack/legacy/plugins/siem/cypress.json
deleted file mode 100644
index a0333a1068146..0000000000000
--- a/x-pack/legacy/plugins/siem/cypress.json
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "baseUrl": "http://localhost:5601",
- "defaultCommandTimeout": 120000,
- "screenshotsFolder": "../../../../target/kibana-siem/cypress/screenshots",
- "trashAssetsBeforeRuns": false,
- "video": false,
- "videosFolder": "../../../../target/kibana-siem/cypress/videos"
-}
diff --git a/x-pack/legacy/plugins/siem/index.ts b/x-pack/legacy/plugins/siem/index.ts
index 3773283555b32..6e03583dda69f 100644
--- a/x-pack/legacy/plugins/siem/index.ts
+++ b/x-pack/legacy/plugins/siem/index.ts
@@ -6,11 +6,10 @@
import { i18n } from '@kbn/i18n';
import { resolve } from 'path';
-import { Server } from 'hapi';
import { Root } from 'joi';
-import { plugin } from './server';
-import { savedObjectMappings } from './server/saved_objects';
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { savedObjectMappings } from '../../../plugins/siem/server/saved_objects';
import {
APP_ID,
@@ -23,15 +22,13 @@ import {
DEFAULT_INTERVAL_VALUE,
DEFAULT_FROM,
DEFAULT_TO,
- DEFAULT_SIGNALS_INDEX,
ENABLE_NEWS_FEED_SETTING,
NEWS_FEED_URL_SETTING,
NEWS_FEED_URL_SETTING_DEFAULT,
- SIGNALS_INDEX_KEY,
IP_REPUTATION_LINKS_SETTING,
IP_REPUTATION_LINKS_SETTING_DEFAULT,
-} from './common/constants';
-import { defaultIndexPattern } from './default_index_pattern';
+ DEFAULT_INDEX_PATTERN,
+} from '../../../plugins/siem/common/constants';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -102,7 +99,7 @@ export const siem = (kibana: any) => {
name: i18n.translate('xpack.siem.uiSettings.defaultIndexLabel', {
defaultMessage: 'Elasticsearch indices',
}),
- value: defaultIndexPattern,
+ value: DEFAULT_INDEX_PATTERN,
description: i18n.translate('xpack.siem.uiSettings.defaultIndexDescription', {
defaultMessage:
'Comma-delimited list of Elasticsearch indices from which the SIEM app collects events.
',
@@ -162,31 +159,12 @@ export const siem = (kibana: any) => {
},
mappings: savedObjectMappings,
},
- init(server: Server) {
- const { coreContext, env, setup, start } = server.newPlatform;
- const initializerContext = { ...coreContext, env };
- const __legacy = {
- config: server.config,
- route: server.route.bind(server),
- };
-
- // @ts-ignore-next-line: NewPlatform shim is too loosely typed
- const pluginInstance = plugin(initializerContext);
- // @ts-ignore-next-line: NewPlatform shim is too loosely typed
- pluginInstance.setup(setup.core, setup.plugins, __legacy);
- // @ts-ignore-next-line: NewPlatform shim is too loosely typed
- pluginInstance.start(start.core, start.plugins);
- },
config(Joi: Root) {
- // See x-pack/plugins/siem/server/config.ts if you're adding another
- // value where the configuration has to be duplicated at the moment.
- // When we move over to the new platform completely this will be
- // removed and only server/config.ts should be used.
return Joi.object()
.keys({
enabled: Joi.boolean().default(true),
- [SIGNALS_INDEX_KEY]: Joi.string().default(DEFAULT_SIGNALS_INDEX),
})
+ .unknown(true)
.default();
},
});
diff --git a/x-pack/legacy/plugins/siem/package.json b/x-pack/legacy/plugins/siem/package.json
index 472a473842f02..3a93beef963a0 100644
--- a/x-pack/legacy/plugins/siem/package.json
+++ b/x-pack/legacy/plugins/siem/package.json
@@ -1,16 +1,10 @@
{
"author": "Elastic",
- "name": "siem",
+ "name": "siem-legacy-ui",
"version": "8.0.0",
"private": true,
"license": "Elastic-License",
- "scripts": {
- "extract-mitre-attacks": "node scripts/extract_tactics_techniques_mitre.js & node ../../../../scripts/eslint ./public/pages/detection_engine/mitre/mitre_tactics_techniques.ts --fix",
- "build-graphql-types": "node scripts/generate_types_from_graphql.js",
- "cypress:open": "../../../node_modules/.bin/cypress open",
- "cypress:run": "../../../node_modules/.bin/cypress run --spec ./cypress/integration/**/*.spec.ts --reporter ../../../node_modules/cypress-multi-reporters --reporter-options configFile=./reporter_config.json; status=$?; ../../../node_modules/.bin/mochawesome-merge --reportDir ../../../../target/kibana-siem/cypress/results > ../../../../target/kibana-siem/cypress/results/output.json; ../../../../node_modules/.bin/marge ../../../../target/kibana-siem/cypress/results/output.json --reportDir ../../../../target/kibana-siem/cypress/results; mkdir -p ../../../../target/junit && cp ../../../../target/kibana-siem/cypress/results/*.xml ../../../../target/junit/ && exit $status;",
- "cypress:run-as-ci": "node ../../../../scripts/functional_tests --config ../../../test/siem_cypress/config.ts"
- },
+ "scripts": {},
"devDependencies": {
"@types/lodash": "^4.14.110",
"@types/js-yaml": "^3.12.1",
diff --git a/x-pack/legacy/plugins/siem/public/app/app.tsx b/x-pack/legacy/plugins/siem/public/app/app.tsx
index 7413aeab549db..44c1c923cd6ee 100644
--- a/x-pack/legacy/plugins/siem/public/app/app.tsx
+++ b/x-pack/legacy/plugins/siem/public/app/app.tsx
@@ -20,7 +20,7 @@ import { pluck } from 'rxjs/operators';
import { KibanaContextProvider, useKibana, useUiSetting$ } from '../lib/kibana';
import { Storage } from '../../../../../../src/plugins/kibana_utils/public';
-import { DEFAULT_DARK_MODE } from '../../common/constants';
+import { DEFAULT_DARK_MODE } from '../../../../../plugins/siem/common/constants';
import { ErrorToastDispatcher } from '../components/error_toast_dispatcher';
import { compose } from '../lib/compose/kibana_compose';
import { AppFrontendLibs, AppApolloClient } from '../lib/lib';
diff --git a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx
index 587002c24d526..778adc708d901 100644
--- a/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/alerts_viewer/index.tsx
@@ -6,11 +6,11 @@
import React, { useEffect, useCallback, useMemo } from 'react';
import numeral from '@elastic/numeral';
+import { DEFAULT_NUMBER_FORMAT } from '../../../../../../plugins/siem/common/constants';
import { AlertsComponentsQueryProps } from './types';
import { AlertsTable } from './alerts_table';
import * as i18n from './translations';
import { useUiSetting$ } from '../../lib/kibana';
-import { DEFAULT_NUMBER_FORMAT } from '../../../common/constants';
import { MatrixHistogramContainer } from '../matrix_histogram';
import { histogramConfigs } from './histogram_configs';
import { MatrixHisrogramConfigs } from '../matrix_histogram/types';
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
index 272c41833f368..635d48cca10fc 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.test.tsx
@@ -4,15 +4,29 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { shallow, ShallowWrapper } from 'enzyme';
+import { Chart, BarSeries, Axis, ScaleType } from '@elastic/charts';
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, ReactWrapper, shallow, ShallowWrapper } from 'enzyme';
import React from 'react';
+import { ThemeProvider } from 'styled-components';
+
+import { escapeDataProviderId } from '../drag_and_drop/helpers';
+import { TestProviders } from '../../mock';
import { BarChartBaseComponent, BarChartComponent } from './barchart';
import { ChartSeriesData } from './common';
-import { Chart, BarSeries, Axis, ScaleType } from '@elastic/charts';
jest.mock('../../lib/kibana');
+jest.mock('uuid', () => {
+ return {
+ v1: jest.fn(() => 'uuid.v1()'),
+ v4: jest.fn(() => 'uuid.v4()'),
+ };
+});
+
+const theme = () => ({ eui: euiDarkVars, darkMode: true });
+
const customHeight = '100px';
const customWidth = '120px';
const chartDataSets = [
@@ -116,6 +130,19 @@ const mockConfig = {
customHeight: 324,
};
+// Suppress warnings about "react-beautiful-dnd"
+/* eslint-disable no-console */
+const originalError = console.error;
+const originalWarn = console.warn;
+beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+});
+afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+});
+
describe('BarChartBaseComponent', () => {
let shallowWrapper: ShallowWrapper;
const mockBarChartData: ChartSeriesData[] = [
@@ -280,6 +307,91 @@ describe.each(chartDataSets)('BarChart with valid data [%o]', data => {
expect(shallowWrapper.find('BarChartBase')).toHaveLength(1);
expect(shallowWrapper.find('ChartPlaceHolder')).toHaveLength(0);
});
+
+ it('it does NOT render a draggable legend because stackByField is not provided', () => {
+ expect(shallowWrapper.find('[data-test-subj="draggable-legend"]').exists()).toBe(false);
+ });
+});
+
+describe.each(chartDataSets)('BarChart with stackByField', () => {
+ let wrapper: ReactWrapper;
+
+ const data = [
+ {
+ key: 'python.exe',
+ value: [
+ {
+ x: 1586754900000,
+ y: 9675,
+ g: 'python.exe',
+ },
+ ],
+ },
+ {
+ key: 'kernel',
+ value: [
+ {
+ x: 1586754900000,
+ y: 8708,
+ g: 'kernel',
+ },
+ {
+ x: 1586757600000,
+ y: 9282,
+ g: 'kernel',
+ },
+ ],
+ },
+ {
+ key: 'sshd',
+ value: [
+ {
+ x: 1586754900000,
+ y: 5907,
+ g: 'sshd',
+ },
+ ],
+ },
+ ];
+
+ const expectedColors = ['#1EA593', '#2B70F7', '#CE0060'];
+
+ const stackByField = 'process.name';
+
+ beforeAll(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it('it renders a draggable legend', () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').exists()).toBe(true);
+ });
+
+ expectedColors.forEach((color, i) => {
+ test(`it renders the expected legend color ${color} for legend item ${i}`, () => {
+ expect(wrapper.find(`div [color="${color}"]`).exists()).toBe(true);
+ });
+ });
+
+ data.forEach(datum => {
+ test(`it renders the expected draggable legend text for datum ${datum.key}`, () => {
+ const dataProviderId = `draggableId.content.draggable-legend-item-uuid_v4()-${escapeDataProviderId(
+ stackByField
+ )}-${escapeDataProviderId(datum.key)}`;
+
+ expect(
+ wrapper
+ .find(`div [data-rbd-draggable-id="${dataProviderId}"]`)
+ .first()
+ .text()
+ ).toEqual(datum.key);
+ });
+ });
});
describe.each(chartHolderDataSets)('BarChart with invalid data [%o]', data => {
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
index 2ae0e05850a37..64d15cd6731cb 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/barchart.tsx
@@ -4,13 +4,19 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React from 'react';
+import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
+import React, { useMemo } from 'react';
import { Chart, BarSeries, Axis, Position, ScaleType, Settings } from '@elastic/charts';
import { getOr, get, isNumber } from 'lodash/fp';
import deepmerge from 'deepmerge';
+import uuid from 'uuid';
+import styled from 'styled-components';
-import { useThrottledResizeObserver } from '../utils';
+import { escapeDataProviderId } from '../drag_and_drop/helpers';
import { useTimeZone } from '../../lib/kibana';
+import { defaultLegendColors } from '../matrix_histogram/utils';
+import { useThrottledResizeObserver } from '../utils';
+
import { ChartPlaceHolder } from './chart_place_holder';
import {
chartDefaultSettings,
@@ -22,6 +28,12 @@ import {
WrappedByAutoSizer,
useTheme,
} from './common';
+import { DraggableLegend } from './draggable_legend';
+import { LegendItem } from './draggable_legend_item';
+
+const LegendFlexItem = styled(EuiFlexItem)`
+ overview: hidden;
+`;
const checkIfAllTheDataInTheSeriesAreValid = (series: ChartSeriesData): series is ChartSeriesData =>
series != null &&
@@ -38,12 +50,14 @@ const checkIfAnyValidSeriesExist = (
// Bar chart rotation: https://ela.st/chart-rotations
export const BarChartBaseComponent = ({
data,
+ forceHiddenLegend = false,
...chartConfigs
}: {
data: ChartSeriesData[];
width: string | null | undefined;
height: string | null | undefined;
configs?: ChartSeriesConfigs | undefined;
+ forceHiddenLegend?: boolean;
}) => {
const theme = useTheme();
const timeZone = useTimeZone();
@@ -59,10 +73,10 @@ export const BarChartBaseComponent = ({
return chartConfigs.width && chartConfigs.height ? (
-
+
{data.map(series => {
const barSeriesKey = series.key;
- return checkIfAllTheDataInTheSeriesAreValid ? (
+ return checkIfAllTheDataInTheSeriesAreValid(series) ? (
= ({ barChart, configs }) => {
+const NO_LEGEND_DATA: LegendItem[] = [];
+
+export const BarChartComponent: React.FC = ({
+ barChart,
+ configs,
+ stackByField,
+}) => {
const { ref: measureRef, width, height } = useThrottledResizeObserver();
+ const legendItems: LegendItem[] = useMemo(
+ () =>
+ barChart != null && stackByField != null
+ ? barChart.map((d, i) => ({
+ color: d.color ?? i < defaultLegendColors.length ? defaultLegendColors[i] : undefined,
+ dataProviderId: escapeDataProviderId(
+ `draggable-legend-item-${uuid.v4()}-${stackByField}-${d.key}`
+ ),
+ field: stackByField,
+ value: d.key,
+ }))
+ : NO_LEGEND_DATA,
+ [barChart, stackByField]
+ );
+
const customHeight = get('customHeight', configs);
const customWidth = get('customWidth', configs);
const chartHeight = getChartHeight(customHeight, height);
const chartWidth = getChartWidth(customWidth, width);
return checkIfAnyValidSeriesExist(barChart) ? (
-
-
-
+
+
+
+
+
+
+
+
+
+
) : (
);
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
index d8429cba1b4fb..c7b40c50ffde8 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/charts/common.tsx
@@ -19,8 +19,8 @@ import {
import React, { useMemo } from 'react';
import styled from 'styled-components';
+import { DEFAULT_DARK_MODE } from '../../../../../../plugins/siem/common/constants';
import { useUiSetting } from '../../lib/kibana';
-import { DEFAULT_DARK_MODE } from '../../../common/constants';
export const defaultChartHeight = '100%';
export const defaultChartWidth = '100%';
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.test.tsx
new file mode 100644
index 0000000000000..0da0c2bdc35f2
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.test.tsx
@@ -0,0 +1,149 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, ReactWrapper } from 'enzyme';
+import React from 'react';
+import { ThemeProvider } from 'styled-components';
+
+import { TestProviders } from '../../mock';
+
+import { MIN_LEGEND_HEIGHT, DraggableLegend } from './draggable_legend';
+import { LegendItem } from './draggable_legend_item';
+
+const theme = () => ({ eui: euiDarkVars, darkMode: true });
+
+const allOthersDataProviderId =
+ 'draggable-legend-item-527adabe-8e1c-4a1f-965c-2f3d65dda9e1-event_dataset-All others';
+
+const legendItems: LegendItem[] = [
+ {
+ color: '#1EA593',
+ dataProviderId: 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
+ field: 'event.dataset',
+ value: 'flow',
+ },
+ {
+ color: '#2B70F7',
+ dataProviderId:
+ 'draggable-legend-item-83f6c824-811d-4ec8-b373-eba2b0de6398-event_dataset-suricata_eve',
+ field: 'event.dataset',
+ value: 'suricata.eve',
+ },
+ {
+ color: '#CE0060',
+ dataProviderId:
+ 'draggable-legend-item-ec57bb8f-82cd-4e07-bd38-1d11b3f0ee5f-event_dataset-traefik_access',
+ field: 'event.dataset',
+ value: 'traefik.access',
+ },
+ {
+ color: '#38007E',
+ dataProviderId:
+ 'draggable-legend-item-25d5fcd6-87ba-46b5-893e-c655d7d504e3-event_dataset-esensor',
+ field: 'event.dataset',
+ value: 'esensor',
+ },
+ {
+ color: '#F37020',
+ dataProviderId: allOthersDataProviderId,
+ field: 'event.dataset',
+ value: 'All others',
+ },
+];
+
+describe('DraggableLegend', () => {
+ const height = 400;
+
+ // Suppress warnings about "react-beautiful-dnd"
+ /* eslint-disable no-console */
+ const originalError = console.error;
+ const originalWarn = console.warn;
+ beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+ });
+ afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+ });
+
+ describe('rendering', () => {
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it(`renders a container with the specified non-zero 'height'`, () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
+ 'height',
+ `${height}px`
+ );
+ });
+
+ it('scrolls when necessary', () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
+ 'overflow',
+ 'auto'
+ );
+ });
+
+ it('renders the legend items', () => {
+ legendItems.forEach(item =>
+ expect(
+ wrapper
+ .find(
+ item.dataProviderId !== allOthersDataProviderId
+ ? `[data-test-subj="legend-item-${item.dataProviderId}"]`
+ : '[data-test-subj="all-others-legend-item"]'
+ )
+ .first()
+ .text()
+ ).toEqual(item.value)
+ );
+ });
+
+ it('renders a spacer for every legend item', () => {
+ expect(wrapper.find('[data-test-subj="draggable-legend-spacer"]').hostNodes().length).toEqual(
+ legendItems.length
+ );
+ });
+ });
+
+ it('does NOT render the legend when an empty collection of legendItems is provided', () => {
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').exists()).toBe(false);
+ });
+
+ it(`renders a legend with the minimum height when 'height' is zero`, () => {
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="draggable-legend"]').first()).toHaveStyleRule(
+ 'height',
+ `${MIN_LEGEND_HEIGHT}px`
+ );
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.tsx
new file mode 100644
index 0000000000000..ef3fbb8780d15
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend.tsx
@@ -0,0 +1,68 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiText } from '@elastic/eui';
+import { rgba } from 'polished';
+import React from 'react';
+import styled from 'styled-components';
+
+import { DraggableLegendItem, LegendItem } from './draggable_legend_item';
+
+export const MIN_LEGEND_HEIGHT = 175;
+
+const DraggableLegendContainer = styled.div<{ height: number }>`
+ height: ${({ height }) => `${height}px`};
+ overflow: auto;
+ scrollbar-width: thin;
+ width: 165px;
+
+ &::-webkit-scrollbar {
+ height: ${({ theme }) => theme.eui.euiScrollBar};
+ width: ${({ theme }) => theme.eui.euiScrollBar};
+ }
+
+ &::-webkit-scrollbar-thumb {
+ background-clip: content-box;
+ background-color: ${({ theme }) => rgba(theme.eui.euiColorDarkShade, 0.5)};
+ border: ${({ theme }) => theme.eui.euiScrollBarCorner} solid transparent;
+ }
+
+ &::-webkit-scrollbar-corner,
+ &::-webkit-scrollbar-track {
+ background-color: transparent;
+ }
+`;
+
+const DraggableLegendComponent: React.FC<{
+ height: number;
+ legendItems: LegendItem[];
+}> = ({ height, legendItems }) => {
+ if (legendItems.length === 0) {
+ return null;
+ }
+
+ return (
+
+
+
+ {legendItems.map(item => (
+
+
+
+
+ ))}
+
+
+
+ );
+};
+
+DraggableLegendComponent.displayName = 'DraggableLegendComponent';
+
+export const DraggableLegend = React.memo(DraggableLegendComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.test.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.test.tsx
new file mode 100644
index 0000000000000..581952a8415f6
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.test.tsx
@@ -0,0 +1,143 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
+import { mount, ReactWrapper } from 'enzyme';
+import React from 'react';
+import { ThemeProvider } from 'styled-components';
+
+import { TestProviders } from '../../mock';
+
+import { DraggableLegendItem, LegendItem } from './draggable_legend_item';
+
+const theme = () => ({ eui: euiDarkVars, darkMode: true });
+
+describe('DraggableLegendItem', () => {
+ // Suppress warnings about "react-beautiful-dnd"
+ /* eslint-disable no-console */
+ const originalError = console.error;
+ const originalWarn = console.warn;
+ beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+ });
+ afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+ });
+
+ describe('rendering a regular (non "All others") legend item', () => {
+ const legendItem: LegendItem = {
+ color: '#1EA593',
+ dataProviderId:
+ 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
+ field: 'event.dataset',
+ value: 'flow',
+ };
+
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it('renders a colored circle with the expected legend item color', () => {
+ expect(
+ wrapper
+ .find('[data-test-subj="legend-color"]')
+ .first()
+ .props().color
+ ).toEqual(legendItem.color);
+ });
+
+ it('renders draggable legend item text', () => {
+ expect(
+ wrapper
+ .find(`[data-test-subj="legend-item-${legendItem.dataProviderId}"]`)
+ .first()
+ .text()
+ ).toEqual(legendItem.value);
+ });
+
+ it('does NOT render a non-draggable "All others" legend item', () => {
+ expect(wrapper.find(`[data-test-subj="all-others-legend-item"]`).exists()).toBe(false);
+ });
+ });
+
+ describe('rendering an "All others" legend item', () => {
+ const allOthersLegendItem: LegendItem = {
+ color: '#F37020',
+ dataProviderId:
+ 'draggable-legend-item-527adabe-8e1c-4a1f-965c-2f3d65dda9e1-event_dataset-All others',
+ field: 'event.dataset',
+ value: 'All others',
+ };
+
+ let wrapper: ReactWrapper;
+
+ beforeEach(() => {
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ it('renders a colored circle with the expected legend item color', () => {
+ expect(
+ wrapper
+ .find('[data-test-subj="legend-color"]')
+ .first()
+ .props().color
+ ).toEqual(allOthersLegendItem.color);
+ });
+
+ it('does NOT render a draggable legend item', () => {
+ expect(
+ wrapper
+ .find(`[data-test-subj="legend-item-${allOthersLegendItem.dataProviderId}"]`)
+ .exists()
+ ).toBe(false);
+ });
+
+ it('renders NON-draggable `All others` legend item text', () => {
+ expect(
+ wrapper
+ .find(`[data-test-subj="all-others-legend-item"]`)
+ .first()
+ .text()
+ ).toEqual(allOthersLegendItem.value);
+ });
+ });
+
+ it('does NOT render a colored circle when the legend item has no color', () => {
+ const noColorLegendItem: LegendItem = {
+ // no `color` attribute for this `LegendItem`!
+ dataProviderId:
+ 'draggable-legend-item-3207fda7-d008-402a-86a0-8ad632081bad-event_dataset-flow',
+ field: 'event.dataset',
+ value: 'flow',
+ };
+
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="legend-color"]').exists()).toBe(false);
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.tsx b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.tsx
new file mode 100644
index 0000000000000..cdda1733932d5
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/charts/draggable_legend_item.tsx
@@ -0,0 +1,62 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiFlexGroup, EuiFlexItem, EuiHealth, EuiText } from '@elastic/eui';
+import React from 'react';
+import styled from 'styled-components';
+
+import { DefaultDraggable } from '../draggables';
+
+import * as i18n from './translation';
+
+// The "All others" legend item is not draggable
+const AllOthers = styled.span`
+ padding-left: 7px;
+`;
+
+export interface LegendItem {
+ color?: string;
+ dataProviderId: string;
+ field: string;
+ value: string;
+}
+
+const DraggableLegendItemComponent: React.FC<{
+ legendItem: LegendItem;
+}> = ({ legendItem }) => {
+ const { color, dataProviderId, field, value } = legendItem;
+
+ return (
+
+
+ {color != null && (
+
+
+
+ )}
+
+
+ {value !== i18n.ALL_OTHERS ? (
+
+ ) : (
+ <>
+ {value}
+ >
+ )}
+
+
+
+ );
+};
+
+DraggableLegendItemComponent.displayName = 'DraggableLegendItemComponent';
+
+export const DraggableLegendItem = React.memo(DraggableLegendItemComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/charts/translation.ts b/x-pack/legacy/plugins/siem/public/components/charts/translation.ts
index 341cb7782f87c..891f59fc97bd1 100644
--- a/x-pack/legacy/plugins/siem/public/components/charts/translation.ts
+++ b/x-pack/legacy/plugins/siem/public/components/charts/translation.ts
@@ -13,3 +13,7 @@ export const ALL_VALUES_ZEROS_TITLE = i18n.translate('xpack.siem.chart.dataAllVa
export const DATA_NOT_AVAILABLE_TITLE = i18n.translate('xpack.siem.chart.dataNotAvailableTitle', {
defaultMessage: 'Chart Data Not Available',
});
+
+export const ALL_OTHERS = i18n.translate('xpack.siem.chart.allOthersGroupingLabel', {
+ defaultMessage: 'All others',
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx
index 11db33fff6d72..248ae671550ef 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/drag_drop_context_wrapper.tsx
@@ -27,6 +27,9 @@ import {
draggableIsField,
} from './helpers';
+// @ts-ignore
+window['__react-beautiful-dnd-disable-dev-warnings'] = true;
+
interface Props {
browserFields: BrowserFields;
children: React.ReactNode;
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx
index 11891afabbf3d..cd9e1dc95ff01 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.test.tsx
@@ -7,12 +7,13 @@
import { shallow } from 'enzyme';
import React from 'react';
import { MockedProvider } from 'react-apollo/test-utils';
+import { DraggableStateSnapshot, DraggingStyle } from 'react-beautiful-dnd';
import { mockBrowserFields, mocksSource } from '../../containers/source/mock';
import { TestProviders } from '../../mock';
import { mockDataProviders } from '../timeline/data_providers/mock/mock_data_providers';
import { DragDropContextWrapper } from './drag_drop_context_wrapper';
-import { DraggableWrapper, ConditionalPortal } from './draggable_wrapper';
+import { ConditionalPortal, DraggableWrapper, getStyle } from './draggable_wrapper';
import { useMountAppended } from '../../utils/use_mount_appended';
describe('DraggableWrapper', () => {
@@ -48,6 +49,36 @@ describe('DraggableWrapper', () => {
expect(wrapper.text()).toEqual(message);
});
+
+ test('it does NOT render hover actions when the mouse is NOT over the draggable wrapper', () => {
+ const wrapper = mount(
+
+
+
+ message} />
+
+
+
+ );
+
+ expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(false);
+ });
+
+ test('it renders hover actions when the mouse is over the draggable wrapper', () => {
+ const wrapper = mount(
+
+
+
+ message} />
+
+
+
+ );
+
+ wrapper.simulate('mouseenter');
+ wrapper.update();
+ expect(wrapper.find('[data-test-subj="copy-to-clipboard"]').exists()).toBe(true);
+ });
});
describe('text truncation styling', () => {
@@ -100,4 +131,36 @@ describe('ConditionalPortal', () => {
expect(props.registerProvider.mock.calls.length).toEqual(1);
});
+
+ describe('getStyle', () => {
+ const style: DraggingStyle = {
+ boxSizing: 'border-box',
+ height: 10,
+ left: 1,
+ pointerEvents: 'none',
+ position: 'fixed',
+ transition: 'none',
+ top: 123,
+ width: 50,
+ zIndex: 9999,
+ };
+
+ it('returns a style with no transitionDuration when the snapshot is not drop animating', () => {
+ const snapshot: DraggableStateSnapshot = {
+ isDragging: true,
+ isDropAnimating: false, // <-- NOT drop animating
+ };
+
+ expect(getStyle(style, snapshot)).not.toHaveProperty('transitionDuration');
+ });
+
+ it('returns a style with a transitionDuration when the snapshot is drop animating', () => {
+ const snapshot: DraggableStateSnapshot = {
+ isDragging: true,
+ isDropAnimating: true, // <-- it is drop animating
+ };
+
+ expect(getStyle(style, snapshot)).toHaveProperty('transitionDuration', '0.00000001s');
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
index 3a6a4de7984db..c7da5b5c58951 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper.tsx
@@ -4,12 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import React, { useCallback, useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
Draggable,
DraggableProvided,
DraggableStateSnapshot,
+ DraggingStyle,
Droppable,
+ NotDraggingStyle,
} from 'react-beautiful-dnd';
import { useDispatch } from 'react-redux';
import styled from 'styled-components';
@@ -18,6 +20,9 @@ import deepEqual from 'fast-deep-equal';
import { dragAndDropActions } from '../../store/drag_and_drop';
import { DataProvider } from '../timeline/data_providers/data_provider';
import { TruncatableText } from '../truncatable_text';
+import { WithHoverActions } from '../with_hover_actions';
+
+import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content';
import { getDraggableId, getDroppableId } from './helpers';
import { ProviderContainer } from './provider_container';
@@ -67,23 +72,42 @@ type RenderFunctionProp = (
state: DraggableStateSnapshot
) => React.ReactNode;
-interface OwnProps {
+interface Props {
dataProvider: DataProvider;
inline?: boolean;
render: RenderFunctionProp;
truncate?: boolean;
+ onFilterAdded?: () => void;
}
-type Props = OwnProps;
-
/**
* Wraps a draggable component to handle registration / unregistration of the
* data provider associated with the item being dropped
*/
+export const getStyle = (
+ style: DraggingStyle | NotDraggingStyle | undefined,
+ snapshot: DraggableStateSnapshot
+) => {
+ if (!snapshot.isDropAnimating) {
+ return style;
+ }
+
+ return {
+ ...style,
+ transitionDuration: '0.00000001s', // cannot be 0, but can be a very short duration
+ };
+};
+
export const DraggableWrapper = React.memo(
- ({ dataProvider, render, truncate }) => {
+ ({ dataProvider, onFilterAdded, render, truncate }) => {
+ const [showTopN, setShowTopN] = useState(false);
+ const toggleTopN = useCallback(() => {
+ setShowTopN(!showTopN);
+ }, [setShowTopN, showTopN]);
+
const [providerRegistered, setProviderRegistered] = useState(false);
+
const dispatch = useDispatch();
const registerProvider = useCallback(() => {
@@ -105,65 +129,90 @@ export const DraggableWrapper = React.memo(
[]
);
- return (
-
-
- (
-
-
-
(
+
+ ),
+ [dataProvider, onFilterAdded, showTopN, toggleTopN]
+ );
+
+ const renderContent = useCallback(
+ () => (
+
+
+ (
+
+
- {render(dataProvider, provided, snapshot)}
-
-
-
- )}
- >
- {droppableProvided => (
-
-
- {(provided, snapshot) => (
-
- {truncate && !snapshot.isDragging ? (
-
- {render(dataProvider, provided, snapshot)}
-
- ) : (
-
- {render(dataProvider, provided, snapshot)}
-
- )}
-
- )}
-
- {droppableProvided.placeholder}
-
- )}
-
-
-
+ {render(dataProvider, provided, snapshot)}
+
+
+
+ )}
+ >
+ {droppableProvided => (
+
+
+ {(provided, snapshot) => (
+
+ {truncate && !snapshot.isDragging ? (
+
+ {render(dataProvider, provided, snapshot)}
+
+ ) : (
+
+ {render(dataProvider, provided, snapshot)}
+
+ )}
+
+ )}
+
+ {droppableProvided.placeholder}
+
+ )}
+
+
+
+ ),
+ [dataProvider, render, registerProvider, truncate]
+ );
+
+ return (
+
);
},
(prevProps, nextProps) =>
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx
new file mode 100644
index 0000000000000..f8b5eb7209ff4
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.test.tsx
@@ -0,0 +1,559 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { mount, ReactWrapper } from 'enzyme';
+import React from 'react';
+import { MockedProvider } from 'react-apollo/test-utils';
+
+import { mocksSource } from '../../containers/source/mock';
+import { wait } from '../../lib/helpers';
+import { useKibana } from '../../lib/kibana';
+import { TestProviders } from '../../mock';
+import { createKibanaCoreStartMock } from '../../mock/kibana_core';
+import { FilterManager } from '../../../../../../../src/plugins/data/public';
+import { TimelineContext } from '../timeline/timeline_context';
+
+import { DraggableWrapperHoverContent } from './draggable_wrapper_hover_content';
+
+jest.mock('../../lib/kibana');
+
+jest.mock('uuid', () => {
+ return {
+ v1: jest.fn(() => 'uuid.v1()'),
+ v4: jest.fn(() => 'uuid.v4()'),
+ };
+});
+
+const mockUiSettingsForFilterManager = createKibanaCoreStartMock().uiSettings;
+const field = 'process.name';
+const value = 'nice';
+
+describe('DraggableWrapperHoverContent', () => {
+ // Suppress warnings about "react-beautiful-dnd"
+ /* eslint-disable no-console */
+ const originalError = console.error;
+ const originalWarn = console.warn;
+ beforeAll(() => {
+ console.warn = jest.fn();
+ console.error = jest.fn();
+ });
+ afterAll(() => {
+ console.error = originalError;
+ console.warn = originalWarn;
+ });
+
+ /**
+ * The tests for "Filter for value" and "Filter out value" are similar enough
+ * to combine them into "table tests" using this array
+ */
+ const forOrOut = ['for', 'out'];
+
+ forOrOut.forEach(hoverAction => {
+ describe(`Filter ${hoverAction} value`, () => {
+ test(`it renders the 'Filter ${hoverAction} value' button when showTopN is false`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it does NOT render the 'Filter ${hoverAction} value' button when showTopN is true`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ describe('when run in the context of a timeline', () => {
+ let filterManager: FilterManager;
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+
+ beforeEach(() => {
+ filterManager = new FilterManager(mockUiSettingsForFilterManager);
+ filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ test('when clicked, it adds a filter to the timeline when running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(filterManager.addFilters).toBeCalledWith({
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: hoverAction === 'out' ? true : false,
+ params: { query: 'nice' },
+ type: 'phrase',
+ value: 'nice',
+ },
+ query: { match: { 'process.name': { query: 'nice', type: 'phrase' } } },
+ });
+ });
+
+ test('when clicked, invokes onFilterAdded when running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(onFilterAdded).toBeCalled();
+ });
+ });
+
+ describe('when NOT run in the context of a timeline', () => {
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+ const kibana = useKibana();
+
+ beforeEach(() => {
+ kibana.services.data.query.filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+ );
+ });
+
+ test('when clicked, it adds a filter to the global filters when NOT running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(kibana.services.data.query.filterManager.addFilters).toBeCalledWith({
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: hoverAction === 'out' ? true : false,
+ params: { query: 'nice' },
+ type: 'phrase',
+ value: 'nice',
+ },
+ query: { match: { 'process.name': { query: 'nice', type: 'phrase' } } },
+ });
+ });
+
+ test('when clicked, invokes onFilterAdded when NOT running in the context of a timeline', () => {
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(onFilterAdded).toBeCalled();
+ });
+ });
+
+ describe('an empty string value when run in the context of a timeline', () => {
+ let filterManager: FilterManager;
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+
+ beforeEach(() => {
+ filterManager = new FilterManager(mockUiSettingsForFilterManager);
+ filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+
+
+ );
+ });
+
+ const expectedFilterTypeDescription =
+ hoverAction === 'for' ? 'a "NOT exists"' : 'an "exists"';
+ test(`when clicked, it adds ${expectedFilterTypeDescription} filter to the timeline when run in the context of a timeline`, () => {
+ const expected =
+ hoverAction === 'for'
+ ? {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: true,
+ type: 'exists',
+ value: 'exists',
+ },
+ }
+ : {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: false,
+ type: 'exists',
+ value: 'exists',
+ },
+ };
+
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(filterManager.addFilters).toBeCalledWith(expected);
+ });
+ });
+
+ describe('an empty string value when NOT run in the context of a timeline', () => {
+ let wrapper: ReactWrapper;
+ let onFilterAdded: () => void;
+ const kibana = useKibana();
+
+ beforeEach(() => {
+ kibana.services.data.query.filterManager.addFilters = jest.fn();
+ onFilterAdded = jest.fn();
+
+ wrapper = mount(
+
+
+
+ );
+ });
+
+ const expectedFilterTypeDescription =
+ hoverAction === 'for' ? 'a "NOT exists"' : 'an "exists"';
+ test(`when clicked, it adds ${expectedFilterTypeDescription} filter to the global filters when NOT running in the context of a timeline`, () => {
+ const expected =
+ hoverAction === 'for'
+ ? {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: true,
+ type: 'exists',
+ value: 'exists',
+ },
+ }
+ : {
+ exists: { field: 'process.name' },
+ meta: {
+ alias: null,
+ disabled: false,
+ key: 'process.name',
+ negate: false,
+ type: 'exists',
+ value: 'exists',
+ },
+ };
+
+ wrapper
+ .find(`[data-test-subj="filter-${hoverAction}-value"]`)
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(kibana.services.data.query.filterManager.addFilters).toBeCalledWith(expected);
+ });
+ });
+ });
+ });
+
+ describe('Top N', () => {
+ test(`it renders the 'Show top field' button when showTopN is false and an aggregatable string field is provided`, async () => {
+ const aggregatableStringField = 'cloud.account.id';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait(); // https://github.com/apollographql/react-apollo/issues/1711
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it renders the 'Show top field' button when showTopN is false and a whitelisted signal field is provided`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it does NOT render the 'Show top field' button when showTopN is false and a field not known to BrowserFields is provided`, async () => {
+ const notKnownToBrowserFields = 'unknown.field';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test(`invokes the toggleTopN function when the 'Show top field' button is clicked`, async () => {
+ const toggleTopN = jest.fn();
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .simulate('click');
+ wrapper.update();
+
+ expect(toggleTopN).toBeCalled();
+ });
+
+ test(`it does NOT render the Top N histogram when when showTopN is false`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="eventsByDatasetOverviewPanel"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test(`it does NOT render the 'Show top field' button when showTopN is true`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="show-top-field"]')
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+
+ test(`it renders the Top N histogram when when showTopN is true`, async () => {
+ const whitelistedField = 'signal.rule.name';
+ const wrapper = mount(
+
+
+
+
+
+ );
+
+ await wait();
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('[data-test-subj="eventsByDatasetOverview-uuid.v4()Panel"]')
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+ });
+
+ describe('Copy to Clipboard', () => {
+ test(`it renders the 'Copy to Clipboard' button when showTopN is false`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="copy-to-clipboard"]`)
+ .first()
+ .exists()
+ ).toBe(true);
+ });
+
+ test(`it does NOT render the 'Copy to Clipboard' button when showTopN is true`, () => {
+ const wrapper = mount(
+
+
+
+ );
+
+ expect(
+ wrapper
+ .find(`[data-test-subj="copy-to-clipboard"]`)
+ .first()
+ .exists()
+ ).toBe(false);
+ });
+ });
+});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.tsx b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.tsx
new file mode 100644
index 0000000000000..40725bea498f1
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/draggable_wrapper_hover_content.tsx
@@ -0,0 +1,145 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { EuiButtonIcon, EuiToolTip } from '@elastic/eui';
+import React, { useCallback, useMemo } from 'react';
+
+import { getAllFieldsByName, WithSource } from '../../containers/source';
+import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
+import { useKibana } from '../../lib/kibana';
+import { createFilter } from '../page/add_filter_to_global_search_bar';
+import { useTimelineContext } from '../timeline/timeline_context';
+import { StatefulTopN } from '../top_n';
+
+import { allowTopN } from './helpers';
+import * as i18n from './translations';
+
+interface Props {
+ field: string;
+ onFilterAdded?: () => void;
+ showTopN: boolean;
+ toggleTopN: () => void;
+ value?: string[] | string | null;
+}
+
+const DraggableWrapperHoverContentComponent: React.FC = ({
+ field,
+ onFilterAdded,
+ showTopN,
+ toggleTopN,
+ value,
+}) => {
+ const kibana = useKibana();
+ const { filterManager: timelineFilterManager } = useTimelineContext();
+ const filterManager = useMemo(() => kibana.services.data.query.filterManager, [
+ kibana.services.data.query.filterManager,
+ ]);
+
+ const filterForValue = useCallback(() => {
+ const filter =
+ value?.length === 0 ? createFilter(field, undefined) : createFilter(field, value);
+ const activeFilterManager = timelineFilterManager ?? filterManager;
+
+ if (activeFilterManager != null) {
+ activeFilterManager.addFilters(filter);
+
+ if (onFilterAdded != null) {
+ onFilterAdded();
+ }
+ }
+ }, [field, value, timelineFilterManager, filterManager, onFilterAdded]);
+
+ const filterOutValue = useCallback(() => {
+ const filter =
+ value?.length === 0 ? createFilter(field, null, false) : createFilter(field, value, true);
+ const activeFilterManager = timelineFilterManager ?? filterManager;
+
+ if (activeFilterManager != null) {
+ activeFilterManager.addFilters(filter);
+
+ if (onFilterAdded != null) {
+ onFilterAdded();
+ }
+ }
+ }, [field, value, timelineFilterManager, filterManager, onFilterAdded]);
+
+ return (
+ <>
+ {!showTopN && value != null && (
+
+
+
+ )}
+
+ {!showTopN && value != null && (
+
+
+
+ )}
+
+
+ {({ browserFields }) => (
+ <>
+ {allowTopN({
+ browserField: getAllFieldsByName(browserFields)[field],
+ fieldName: field,
+ }) && (
+ <>
+ {!showTopN && (
+
+
+
+ )}
+
+ {showTopN && (
+
+ )}
+ >
+ )}
+ >
+ )}
+
+
+ {!showTopN && (
+
+
+
+ )}
+ >
+ );
+};
+
+DraggableWrapperHoverContentComponent.displayName = 'DraggableWrapperHoverContentComponent';
+
+export const DraggableWrapperHoverContent = React.memo(DraggableWrapperHoverContentComponent);
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts
index af4b9b280f3cd..753fa5b54eade 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.test.ts
@@ -4,7 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
+import { omit } from 'lodash/fp';
+
import {
+ allowTopN,
destinationIsTimelineButton,
destinationIsTimelineColumns,
destinationIsTimelineProviders,
@@ -717,4 +720,96 @@ describe('helpers', () => {
expect(escaped).toEqual('hello.how.are.you?');
});
});
+
+ describe('#allowTopN', () => {
+ const aggregatableAllowedType = {
+ category: 'cloud',
+ description:
+ 'The cloud account or organization id used to identify different entities in a multi-tenant environment. Examples: AWS account id, Google Cloud ORG Id, or other unique identifier.',
+ example: '666777888999',
+ indexes: ['auditbeat', 'filebeat', 'packetbeat'],
+ name: 'cloud.account.id',
+ searchable: true,
+ type: 'string',
+ aggregatable: true,
+ format: '',
+ };
+
+ test('it returns true for an aggregatable field that is an allowed type', () => {
+ expect(
+ allowTopN({
+ browserField: aggregatableAllowedType,
+ fieldName: aggregatableAllowedType.name,
+ })
+ ).toBe(true);
+ });
+
+ test('it returns true for a whitelisted non-BrowserField', () => {
+ expect(
+ allowTopN({
+ browserField: undefined,
+ fieldName: 'signal.rule.name',
+ })
+ ).toBe(true);
+ });
+
+ test('it returns false for a NON-aggregatable field that is an allowed type', () => {
+ const nonAggregatableAllowedType = {
+ ...aggregatableAllowedType,
+ aggregatable: false,
+ };
+
+ expect(
+ allowTopN({
+ browserField: nonAggregatableAllowedType,
+ fieldName: nonAggregatableAllowedType.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false for a aggregatable field that is NOT an allowed type', () => {
+ const aggregatableNotAllowedType = {
+ ...aggregatableAllowedType,
+ type: 'not-an-allowed-type',
+ };
+
+ expect(
+ allowTopN({
+ browserField: aggregatableNotAllowedType,
+ fieldName: aggregatableNotAllowedType.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false if the BrowserField is missing the aggregatable property', () => {
+ const missingAggregatable = omit('aggregatable', aggregatableAllowedType);
+
+ expect(
+ allowTopN({
+ browserField: missingAggregatable,
+ fieldName: missingAggregatable.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false if the BrowserField is missing the type property', () => {
+ const missingType = omit('type', aggregatableAllowedType);
+
+ expect(
+ allowTopN({
+ browserField: missingType,
+ fieldName: missingType.name,
+ })
+ ).toBe(false);
+ });
+
+ test('it returns false for a non-whitelisted field when a BrowserField is not provided', () => {
+ expect(
+ allowTopN({
+ browserField: undefined,
+ fieldName: 'non-whitelisted',
+ })
+ ).toBe(false);
+ });
+ });
});
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
index 82ddd2c9f29d7..cd3d7cc68d537 100644
--- a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/helpers.ts
@@ -9,7 +9,7 @@ import { DropResult } from 'react-beautiful-dnd';
import { Dispatch } from 'redux';
import { ActionCreator } from 'typescript-fsa';
-import { BrowserFields, getAllFieldsByName } from '../../containers/source';
+import { BrowserField, BrowserFields, getAllFieldsByName } from '../../containers/source';
import { IdToDataProvider } from '../../store/drag_and_drop/model';
import { ColumnHeaderOptions } from '../../store/timeline/model';
import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
@@ -227,3 +227,98 @@ export const IS_DRAGGING_CLASS_NAME = 'is-dragging';
/** This class is added to the document body while timeline field dragging */
export const IS_TIMELINE_FIELD_DRAGGING_CLASS_NAME = 'is-timeline-field-dragging';
+
+export const allowTopN = ({
+ browserField,
+ fieldName,
+}: {
+ browserField: Partial | undefined;
+ fieldName: string;
+}): boolean => {
+ const isAggregatable = browserField?.aggregatable ?? false;
+ const fieldType = browserField?.type ?? '';
+ const isAllowedType = [
+ 'boolean',
+ 'geo-point',
+ 'geo-shape',
+ 'ip',
+ 'keyword',
+ 'number',
+ 'numeric',
+ 'string',
+ ].includes(fieldType);
+
+ // TODO: remove this explicit whitelist when the ECS documentation includes signals
+ const isWhitelistedNonBrowserField = [
+ 'signal.ancestors.depth',
+ 'signal.ancestors.id',
+ 'signal.ancestors.rule',
+ 'signal.ancestors.type',
+ 'signal.original_event.action',
+ 'signal.original_event.category',
+ 'signal.original_event.code',
+ 'signal.original_event.created',
+ 'signal.original_event.dataset',
+ 'signal.original_event.duration',
+ 'signal.original_event.end',
+ 'signal.original_event.hash',
+ 'signal.original_event.id',
+ 'signal.original_event.kind',
+ 'signal.original_event.module',
+ 'signal.original_event.original',
+ 'signal.original_event.outcome',
+ 'signal.original_event.provider',
+ 'signal.original_event.risk_score',
+ 'signal.original_event.risk_score_norm',
+ 'signal.original_event.sequence',
+ 'signal.original_event.severity',
+ 'signal.original_event.start',
+ 'signal.original_event.timezone',
+ 'signal.original_event.type',
+ 'signal.original_time',
+ 'signal.parent.depth',
+ 'signal.parent.id',
+ 'signal.parent.index',
+ 'signal.parent.rule',
+ 'signal.parent.type',
+ 'signal.rule.created_by',
+ 'signal.rule.description',
+ 'signal.rule.enabled',
+ 'signal.rule.false_positives',
+ 'signal.rule.filters',
+ 'signal.rule.from',
+ 'signal.rule.id',
+ 'signal.rule.immutable',
+ 'signal.rule.index',
+ 'signal.rule.interval',
+ 'signal.rule.language',
+ 'signal.rule.max_signals',
+ 'signal.rule.name',
+ 'signal.rule.note',
+ 'signal.rule.output_index',
+ 'signal.rule.query',
+ 'signal.rule.references',
+ 'signal.rule.risk_score',
+ 'signal.rule.rule_id',
+ 'signal.rule.saved_id',
+ 'signal.rule.severity',
+ 'signal.rule.size',
+ 'signal.rule.tags',
+ 'signal.rule.threat',
+ 'signal.rule.threat.tactic.id',
+ 'signal.rule.threat.tactic.name',
+ 'signal.rule.threat.tactic.reference',
+ 'signal.rule.threat.technique.id',
+ 'signal.rule.threat.technique.name',
+ 'signal.rule.threat.technique.reference',
+ 'signal.rule.timeline_id',
+ 'signal.rule.timeline_title',
+ 'signal.rule.to',
+ 'signal.rule.type',
+ 'signal.rule.updated_by',
+ 'signal.rule.version',
+ 'signal.status',
+ ].includes(fieldName);
+
+ return isWhitelistedNonBrowserField || (isAggregatable && isAllowedType);
+};
diff --git a/x-pack/legacy/plugins/siem/public/components/drag_and_drop/translations.ts b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/translations.ts
new file mode 100644
index 0000000000000..61d036635a250
--- /dev/null
+++ b/x-pack/legacy/plugins/siem/public/components/drag_and_drop/translations.ts
@@ -0,0 +1,33 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License;
+ * you may not use this file except in compliance with the Elastic License.
+ */
+
+import { i18n } from '@kbn/i18n';
+
+export const COPY_TO_CLIPBOARD = i18n.translate('xpack.siem.dragAndDrop.copyToClipboardTooltip', {
+ defaultMessage: 'Copy to Clipboard',
+});
+
+export const FIELD = i18n.translate('xpack.siem.dragAndDrop.fieldLabel', {
+ defaultMessage: 'Field',
+});
+
+export const FILTER_FOR_VALUE = i18n.translate('xpack.siem.dragAndDrop.filterForValueHoverAction', {
+ defaultMessage: 'Filter for value',
+});
+
+export const FILTER_OUT_VALUE = i18n.translate('xpack.siem.dragAndDrop.filterOutValueHoverAction', {
+ defaultMessage: 'Filter out value',
+});
+
+export const CLOSE = i18n.translate('xpack.siem.dragAndDrop.closeButtonLabel', {
+ defaultMessage: 'Close',
+});
+
+export const SHOW_TOP = (fieldName: string) =>
+ i18n.translate('xpack.siem.overview.showTopTooltip', {
+ values: { fieldName },
+ defaultMessage: `Show top {fieldName}`,
+ });
diff --git a/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap
index 63ba13306ecd8..93608a181adff 100644
--- a/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap
+++ b/x-pack/legacy/plugins/siem/public/components/draggables/__snapshots__/index.test.tsx.snap
@@ -10,6 +10,7 @@ exports[`draggables rendering it renders the default Badge 1`] = `
A child of this
diff --git a/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx b/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx
index 1fe6c936d2823..b3811d05eea04 100644
--- a/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/draggables/index.tsx
@@ -8,7 +8,6 @@ import { EuiBadge, EuiToolTip, IconType } from '@elastic/eui';
import React from 'react';
import styled from 'styled-components';
-import { Omit } from '../../../common/utility_types';
import { DragEffects, DraggableWrapper } from '../drag_and_drop/draggable_wrapper';
import { escapeDataProviderId } from '../drag_and_drop/helpers';
import { getEmptyStringTag } from '../empty_value';
@@ -167,7 +166,7 @@ export const DraggableBadge = React.memo(
tooltipContent={tooltipContent}
queryValue={queryValue}
>
-
+
{children ? children : value !== '' ? value : getEmptyStringTag()}
diff --git a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx
index cbb4006bbf933..a7272593c2b27 100644
--- a/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/embeddables/embedded_map.tsx
@@ -14,7 +14,7 @@ import {
EmbeddablePanel,
ErrorEmbeddable,
} from '../../../../../../../src/plugins/embeddable/public';
-import { DEFAULT_INDEX_KEY } from '../../../common/constants';
+import { DEFAULT_INDEX_KEY } from '../../../../../../plugins/siem/common/constants';
import { getIndexPatternTitleIdMapping } from '../../hooks/api/helpers';
import { useIndexPatterns } from '../../hooks/use_index_patterns';
import { Loader } from '../loader';
diff --git a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
index cd94a9fdcb5ac..131a3a63bae30 100644
--- a/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
+++ b/x-pack/legacy/plugins/siem/public/components/event_details/columns.tsx
@@ -21,7 +21,6 @@ import styled from 'styled-components';
import { BrowserFields } from '../../containers/source';
import { ToStringArray } from '../../graphql/types';
-import { WithCopyToClipboard } from '../../lib/clipboard/with_copy_to_clipboard';
import { ColumnHeaderOptions } from '../../store/timeline/model';
import { DragEffects } from '../drag_and_drop/draggable_wrapper';
import { DroppableWrapper } from '../drag_and_drop/droppable_wrapper';
@@ -35,7 +34,6 @@ import { DEFAULT_COLUMN_MIN_WIDTH } from '../timeline/body/constants';
import { MESSAGE_FIELD_NAME } from '../timeline/body/renderers/constants';
import { FormattedFieldValue } from '../timeline/body/renderers/formatted_field';
import { OnUpdateColumns } from '../timeline/events';
-import { WithHoverActions } from '../with_hover_actions';
import { getIconFromType, getExampleText, getColumnsWithTimestamp } from './helpers';
import * as i18n from './translations';
import { EventFieldsData } from './types';
@@ -172,29 +170,18 @@ export const getColumns = ({
component="span"
key={`event-details-value-flex-item-${contextId}-${eventId}-${data.field}-${i}-${value}`}
>
-
-
-
-
-
- }
- render={() =>
- data.field === MESSAGE_FIELD_NAME ? (
-
- ) : (
-
- )
- }
- />
+ {data.field === MESSAGE_FIELD_NAME ? (
+
+ ) : (
+
+ )}