- {requestError ? (
-
- ) : noResults ? (
+ {noResults ? (
) : (
void;
uiState: PersistedState;
- vis: Vis;
+ vis: ExprVis;
visData: any;
visParams: any;
listenOnChange: boolean;
@@ -40,10 +40,9 @@ class VisualizationChart extends React.Component {
private chartDiv = React.createRef();
private containerDiv = React.createRef();
private renderSubject: Rx.Subject<{
- vis: Vis;
+ vis: ExprVis;
visParams: any;
visData: any;
- container: HTMLElement;
}>;
private renderSubscription: Rx.Subscription;
@@ -54,11 +53,9 @@ class VisualizationChart extends React.Component {
const render$ = this.renderSubject.asObservable().pipe(share());
const success$ = render$.pipe(
- filter(
- ({ vis, visData, container }) => vis && container && (!vis.type.requiresSearch || visData)
- ),
+ filter(({ vis, visData }) => vis && (!vis.type.requiresSearch || visData)),
debounceTime(100),
- switchMap(async ({ vis, visData, visParams, container }) => {
+ switchMap(async ({ vis, visData, visParams }) => {
if (!this.visualization) {
// This should never happen, since we only should trigger another rendering
// after this component has mounted and thus the visualization implementation
@@ -66,15 +63,11 @@ class VisualizationChart extends React.Component {
throw new Error('Visualization implementation was not initialized on first render.');
}
- vis.size = [container.clientWidth, container.clientHeight];
- const status = getUpdateStatus(vis.type.requiresUpdateStatus, this, this.props);
- return this.visualization.render(visData, visParams, status);
+ return this.visualization.render(visData, visParams);
})
);
- const requestError$ = render$.pipe(filter(({ vis }) => vis.requestError));
-
- this.renderSubscription = Rx.merge(success$, requestError$).subscribe(() => {
+ this.renderSubscription = success$.subscribe(() => {
if (this.props.onInit) {
this.props.onInit();
}
@@ -145,7 +138,6 @@ class VisualizationChart extends React.Component {
vis: this.props.vis,
visData: this.props.visData,
visParams: this.props.visParams,
- container: this.containerDiv.current,
});
}
}
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts
index 51d839275fd27..05ce68221eaf0 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/get_index_pattern.ts
@@ -28,18 +28,18 @@ import { getUISettings, getSavedObjects } from '../services';
export async function getIndexPattern(
savedVis: VisSavedObject
): Promise {
- if (savedVis.vis.type.name !== 'metrics') {
- return savedVis.vis.indexPattern;
+ if (savedVis.visState.type !== 'metrics') {
+ return savedVis.searchSource!.getField('index');
}
const savedObjectsClient = getSavedObjects().client;
const defaultIndex = getUISettings().get('defaultIndex');
- if (savedVis.vis.params.index_pattern) {
+ if (savedVis.visState.params.index_pattern) {
const indexPatternObjects = await savedObjectsClient.find({
type: 'index-pattern',
fields: ['title', 'fields'],
- search: `"${savedVis.vis.params.index_pattern}"`,
+ search: `"${savedVis.visState.params.index_pattern}"`,
searchFields: ['title'],
});
const [indexPattern] = indexPatternObjects.savedObjects.map(indexPatterns.getFromSavedObject);
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts
index c45e6832dc836..4b21be83f1722 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable.ts
@@ -45,18 +45,19 @@ import { PersistedState } from '../../../../../../../plugins/visualizations/publ
import { buildPipeline } from '../legacy/build_pipeline';
import { Vis } from '../vis';
import { getExpressions, getUiActions } from '../services';
-import { VisSavedObject } from '../types';
+import { VisualizationsStartDeps } from '../plugin';
import { VIS_EVENT_TO_TRIGGER } from './events';
const getKeys = (o: T): Array => Object.keys(o) as Array;
export interface VisualizeEmbeddableConfiguration {
- savedVisualization: VisSavedObject;
+ vis: Vis;
indexPatterns?: IIndexPattern[];
editUrl: string;
editable: boolean;
appState?: { save(): void };
uiState?: PersistedState;
+ uiActions?: VisualizationsStartDeps['uiActions'];
}
export interface VisualizeInput extends EmbeddableInput {
@@ -73,7 +74,6 @@ export interface VisualizeInput extends EmbeddableInput {
export interface VisualizeOutput extends EmbeddableOutput {
editUrl: string;
indexPatterns?: IIndexPattern[];
- savedObjectId: string;
visTypeName: string;
}
@@ -81,9 +81,6 @@ type ExpressionLoader = InstanceType;
export class VisualizeEmbeddable extends Embeddable {
private handler?: ExpressionLoader;
- private savedVisualization: VisSavedObject;
- private appState: { save(): void } | undefined;
- private uiState: PersistedState;
private timefilter: TimefilterContract;
private timeRange?: TimeRange;
private query?: Query;
@@ -99,49 +96,25 @@ export class VisualizeEmbeddable extends Embeddable {
@@ -184,16 +157,16 @@ export class VisualizeEmbeddable extends Embeddable {
- this.uiState.set(key, visCustomizations[key]);
+ this.vis.uiState.set(key, visCustomizations[key]);
});
- this.uiState.on('change', this.uiStateChangeHandler);
+ this.vis.uiState.on('change', this.uiStateChangeHandler);
}
- } else if (!this.appState) {
- this.uiState.clearAllKeys();
+ } else if (this.parent) {
+ this.vis.uiState.clearAllKeys();
}
}
@@ -227,8 +200,8 @@ export class VisualizeEmbeddable extends Embeddable {
+ const visTypesWithoutInspector = [
+ 'markdown',
+ 'input_control_vis',
+ 'metrics',
+ 'vega',
+ 'timelion',
+ ];
+ if (visTypesWithoutInspector.includes(this.vis.type.name)) {
+ return false;
+ }
+ return this.getInspectorAdapters();
+ };
+
/**
*
* @param {Element} domNode
@@ -245,26 +234,6 @@ export class VisualizeEmbeddable extends Embeddable {
- const visTypesWithoutInspector = [
- 'markdown',
- 'input_control_vis',
- 'metrics',
- 'vega',
- 'timelion',
- ];
- if (visTypesWithoutInspector.includes(this.vis.type.name)) {
- return false;
- }
- return this.getInspectorAdapters();
- };
-
- this.vis.openInspector = this.openInspector;
-
const div = document.createElement('div');
div.className = `visualize panel-content panel-content--fullWidth`;
domNode.appendChild(div);
@@ -277,12 +246,12 @@ export class VisualizeEmbeddable extends Embeddable {
// maps hack, remove once esaggs function is cleaned up and ready to accept variables
if (event.name === 'bounds') {
- const agg = this.vis.getAggConfig().aggs.find((a: any) => {
+ const agg = this.vis.data.aggs!.aggs.find((a: any) => {
return get(a, 'type.dslName') === 'geohash_grid';
});
if (
- agg.params.precision !== event.data.precision ||
- !_.isEqual(agg.params.boundingBox, event.data.boundingBox)
+ (agg && agg.params.precision !== event.data.precision) ||
+ (agg && !_.isEqual(agg.params.boundingBox, event.data.boundingBox))
) {
agg.params.boundingBox = event.data.boundingBox;
agg.params.precision = event.data.precision;
@@ -296,9 +265,10 @@ export class VisualizeEmbeddable extends Embeddable s.unsubscribe());
- this.uiState.off('change', this.uiStateChangeHandler);
- this.savedVisualization.vis.removeListener('reload', this.reload);
- this.savedVisualization.vis.removeListener('update', this.handleVisUpdate);
- this.savedVisualization.destroy();
+ this.vis.uiState.off('change', this.uiStateChangeHandler);
+
if (this.handler) {
this.handler.destroy();
this.handler.getElement().remove();
@@ -361,35 +329,25 @@ export class VisualizeEmbeddable extends Embeddable {
- if (this.appState) {
- this.appState.save();
- }
-
this.updateHandler();
};
private uiStateChangeHandler = () => {
this.updateInput({
- ...this.uiState.toJSON(),
+ ...this.vis.uiState.toJSON(),
});
};
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx
index 1cd97115ee10e..0fd1352f203db 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/embeddable/visualize_embeddable_factory.tsx
@@ -26,9 +26,8 @@ import {
ErrorEmbeddable,
} from '../../../../../../../plugins/embeddable/public';
import { DisabledLabEmbeddable } from './disabled_lab_embeddable';
-import { getIndexPattern } from './get_index_pattern';
import { VisualizeEmbeddable, VisualizeInput, VisualizeOutput } from './visualize_embeddable';
-import { VisSavedObject } from '../types';
+import { Vis } from '../types';
import { VISUALIZE_EMBEDDABLE_TYPE } from './constants';
import {
getCapabilities,
@@ -39,6 +38,8 @@ import {
getTimeFilter,
} from '../services';
import { showNewVisModal } from '../wizard';
+import { VisualizationsStartDeps } from '../plugin';
+import { convertToSerializedVis } from '../saved_visualizations/_saved_vis';
interface VisualizationAttributes extends SavedObjectAttributes {
visState: string;
@@ -52,7 +53,11 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
> {
public readonly type = VISUALIZE_EMBEDDABLE_TYPE;
- constructor() {
+ constructor(
+ private readonly getUiActions: () => Promise<
+ Pick['uiActions']
+ >
+ ) {
super({
savedObjectMetaData: {
name: i18n.translate('visualizations.savedObjectName', { defaultMessage: 'Visualization' }),
@@ -94,36 +99,39 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
}
public async createFromObject(
- savedObject: VisSavedObject,
+ vis: Vis,
input: Partial & { id: string },
parent?: Container
): Promise {
const savedVisualizations = getSavedVisualizationsLoader();
try {
- const visId = savedObject.id as string;
+ const visId = vis.id as string;
const editUrl = visId
? getHttp().basePath.prepend(`/app/kibana${savedVisualizations.urlFor(visId)}`)
: '';
const isLabsEnabled = getUISettings().get('visualize:enableLabs');
- if (!isLabsEnabled && savedObject.vis.type.stage === 'experimental') {
- return new DisabledLabEmbeddable(savedObject.title, input);
+ if (!isLabsEnabled && vis.type.stage === 'experimental') {
+ return new DisabledLabEmbeddable(vis.title, input);
}
- const indexPattern = await getIndexPattern(savedObject);
+ const indexPattern = vis.data.indexPattern;
const indexPatterns = indexPattern ? [indexPattern] : [];
+ const uiActions = await this.getUiActions();
+
const editable = await this.isEditable();
return new VisualizeEmbeddable(
getTimeFilter(),
{
- savedVisualization: savedObject,
+ vis,
indexPatterns,
editUrl,
editable,
appState: input.appState,
uiState: input.uiState,
+ uiActions,
},
input,
parent
@@ -143,7 +151,8 @@ export class VisualizeEmbeddableFactory extends EmbeddableFactory<
try {
const savedObject = await savedVisualizations.get(savedObjectId);
- return this.createFromObject(savedObject, input, parent);
+ const vis = new Vis(savedObject.visState.type, await convertToSerializedVis(savedObject));
+ return this.createFromObject(vis, input, parent);
} catch (e) {
console.error(e); // eslint-disable-line no-console
return new ErrorEmbeddable(e, input, parent);
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.ts
similarity index 67%
rename from src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.js
rename to src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.ts
index a891140677d60..3b0458a6c8dcc 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.js
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/vis.ts
@@ -32,25 +32,47 @@ import _ from 'lodash';
import { PersistedState } from '../../../../../../../plugins/visualizations/public';
import { getTypes } from '../services';
+import { VisType } from '../vis_types';
+import { VisParams } from '../types';
-export class Vis extends EventEmitter {
- constructor(visState = { type: 'histogram' }) {
+export interface ExprVisState {
+ title?: string;
+ type: VisType | string;
+ params?: VisParams;
+}
+
+export interface ExprVisAPIEvents {
+ filter: (data: any) => void;
+ brush: (data: any) => void;
+}
+
+export interface ExprVisAPI {
+ events: ExprVisAPIEvents;
+}
+
+export class ExprVis extends EventEmitter {
+ public title: string = '';
+ public type: VisType;
+ public params: VisParams = {};
+ public sessionState: Record = {};
+ public API: ExprVisAPI;
+ public eventsSubject: any;
+ private uiState: PersistedState;
+
+ constructor(visState: ExprVisState = { type: 'histogram' }) {
super();
- this._setUiState(new PersistedState());
+ this.type = this.getType(visState.type);
+ this.uiState = new PersistedState();
this.setState(visState);
- // Session state is for storing information that is transitory, and will not be saved with the visualization.
- // For instance, map bounds, which depends on the view port, browser window size, etc.
- this.sessionState = {};
-
this.API = {
events: {
- filter: data => {
+ filter: (data: any) => {
if (!this.eventsSubject) return;
this.eventsSubject.next({ name: 'filterBucket', data });
},
- brush: data => {
+ brush: (data: any) => {
if (!this.eventsSubject) return;
this.eventsSubject.next({ name: 'brush', data });
},
@@ -58,18 +80,22 @@ export class Vis extends EventEmitter {
};
}
- setState(state) {
- this.title = state.title || '';
- const type = state.type || this.type;
+ private getType(type: string | VisType) {
if (_.isString(type)) {
- this.type = getTypes().get(type);
+ return getTypes().get(type);
if (!this.type) {
throw new Error(`Invalid type "${type}"`);
}
} else {
- this.type = type;
+ return type;
}
+ }
+ setState(state: ExprVisState) {
+ this.title = state.title || '';
+ if (state.type) {
+ this.type = this.getType(state.type);
+ }
this.params = _.defaultsDeep(
{},
_.cloneDeep(state.params || {}),
@@ -77,10 +103,6 @@ export class Vis extends EventEmitter {
);
}
- setCurrentState(state) {
- this.setState(state);
- }
-
getState() {
return {
title: this.title,
@@ -106,34 +128,27 @@ export class Vis extends EventEmitter {
}
hasUiState() {
- return !!this.__uiState;
+ return !!this.uiState;
}
- /***
- * this should not be used outside of visualize
- * @param uiState
- * @private
- */
- _setUiState(uiState) {
- if (uiState instanceof PersistedState) {
- this.__uiState = uiState;
- }
+ getUiState() {
+ return this.uiState;
}
- getUiState() {
- return this.__uiState;
+ setUiState(state: PersistedState) {
+ this.uiState = state;
}
/**
* Currently this is only used to extract map-specific information
* (e.g. mapZoom, mapCenter).
*/
- uiStateVal(key, val) {
+ uiStateVal(key: string, val: any) {
if (this.hasUiState()) {
if (_.isUndefined(val)) {
- return this.__uiState.get(key);
+ return this.uiState.get(key);
}
- return this.__uiState.set(key, val);
+ return this.uiState.set(key, val);
}
return val;
}
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx
index 02a31447d23c1..0fd81c753da24 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/expressions/visualization_renderer.tsx
@@ -20,8 +20,9 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
// @ts-ignore
-import { Vis } from './vis';
+import { ExprVis } from './vis';
import { Visualization } from '../components';
+import { VisParams } from '../types';
export const visualization = () => ({
name: 'visualization',
@@ -31,9 +32,9 @@ export const visualization = () => ({
const { visData, visConfig, params } = config;
const visType = config.visType || visConfig.type;
- const vis = new Vis({
- type: visType,
- params: visConfig,
+ const vis = new ExprVis({
+ type: visType as string,
+ params: visConfig as VisParams,
});
vis.eventsSubject = { next: handlers.event };
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
index b59eb2277411c..078cc4a3f4035 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/index.ts
@@ -39,12 +39,11 @@ export { VisualizationsSetup, VisualizationsStart };
/** @public types */
export { VisTypeAlias, VisType } from './vis_types';
export { VisSavedObject } from './types';
-export { Vis, VisParams, VisState } from './vis';
+export { Vis, VisParams, SerializedVis, SerializedVisData, VisData } from './vis';
import { VisualizeEmbeddableFactory, VisualizeEmbeddable } from './embeddable';
export type VisualizeEmbeddableFactoryContract = PublicContract;
export type VisualizeEmbeddableContract = PublicContract;
export { TypesService } from './vis_types/types_service';
-export { Status } from './legacy/update_status'; // should remove
export { VISUALIZE_EMBEDDABLE_TYPE, VisualizeInput } from './embeddable';
export { SchemaConfig } from './legacy/build_pipeline';
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts
index 9446069182e19..d5c532b53a53e 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.test.ts
@@ -27,7 +27,7 @@ import {
Schemas,
} from './build_pipeline';
import { Vis } from '..';
-import { searchSourceMock, dataPluginMock } from '../../../../../../../plugins/data/public/mocks';
+import { dataPluginMock } from '../../../../../../../plugins/data/public/mocks';
import { IAggConfig } from '../../../../../../../plugins/data/public';
jest.mock('ui/new_platform');
@@ -78,19 +78,11 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
});
describe('buildPipelineVisFunction', () => {
- let visStateDef: ReturnType;
let schemaConfig: SchemaConfig;
let schemasDef: Schemas;
let uiState: any;
beforeEach(() => {
- visStateDef = {
- title: 'title',
- // @ts-ignore
- type: 'type',
- params: {},
- } as ReturnType;
-
schemaConfig = {
accessor: 0,
label: '',
@@ -105,66 +97,53 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
it('handles vega function', () => {
const vis = {
- ...visStateDef,
params: { spec: 'this is a test' },
};
- const actual = buildPipelineVisFunction.vega(vis, schemasDef, uiState);
+ const actual = buildPipelineVisFunction.vega(vis.params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
it('handles input_control_vis function', () => {
- const visState = {
- ...visStateDef,
- params: {
- some: 'nested',
- data: { here: true },
- },
+ const params = {
+ some: 'nested',
+ data: { here: true },
};
- const actual = buildPipelineVisFunction.input_control_vis(visState, schemasDef, uiState);
+ const actual = buildPipelineVisFunction.input_control_vis(params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
it('handles metrics/tsvb function', () => {
- const visState = { ...visStateDef, params: { foo: 'bar' } };
- const actual = buildPipelineVisFunction.metrics(visState, schemasDef, uiState);
+ const params = { foo: 'bar' };
+ const actual = buildPipelineVisFunction.metrics(params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
it('handles timelion function', () => {
- const visState = {
- ...visStateDef,
- params: { expression: 'foo', interval: 'bar' },
- };
- const actual = buildPipelineVisFunction.timelion(visState, schemasDef, uiState);
+ const params = { expression: 'foo', interval: 'bar' };
+ const actual = buildPipelineVisFunction.timelion(params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
it('handles markdown function', () => {
- const visState = {
- ...visStateDef,
- params: {
- markdown: '## hello _markdown_',
- fontSize: 12,
- openLinksInNewTab: true,
- foo: 'bar',
- },
+ const params = {
+ markdown: '## hello _markdown_',
+ fontSize: 12,
+ openLinksInNewTab: true,
+ foo: 'bar',
};
- const actual = buildPipelineVisFunction.markdown(visState, schemasDef, uiState);
+ const actual = buildPipelineVisFunction.markdown(params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
it('handles undefined markdown function', () => {
- const visState = {
- ...visStateDef,
- params: { fontSize: 12, openLinksInNewTab: true, foo: 'bar' },
- };
- const actual = buildPipelineVisFunction.markdown(visState, schemasDef, uiState);
+ const params = { fontSize: 12, openLinksInNewTab: true, foo: 'bar' };
+ const actual = buildPipelineVisFunction.markdown(params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
describe('handles table function', () => {
it('without splits or buckets', () => {
- const visState = { ...visStateDef, params: { foo: 'bar' } };
+ const params = { foo: 'bar' };
const schemas = {
...schemasDef,
metric: [
@@ -172,22 +151,22 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
{ ...schemaConfig, accessor: 1 },
],
};
- const actual = buildPipelineVisFunction.table(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.table(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
it('with splits', () => {
- const visState = { ...visStateDef, params: { foo: 'bar' } };
+ const params = { foo: 'bar' };
const schemas = {
...schemasDef,
split_row: [1, 2],
};
- const actual = buildPipelineVisFunction.table(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.table(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
it('with splits and buckets', () => {
- const visState = { ...visStateDef, params: { foo: 'bar' } };
+ const params = { foo: 'bar' };
const schemas = {
...schemasDef,
metric: [
@@ -197,17 +176,14 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
split_row: [2, 4],
bucket: [3],
};
- const actual = buildPipelineVisFunction.table(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.table(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
it('with showPartialRows=true and showMetricsAtAllLevels=true', () => {
- const visState = {
- ...visStateDef,
- params: {
- showMetricsAtAllLevels: true,
- showPartialRows: true,
- },
+ const params = {
+ showMetricsAtAllLevels: true,
+ showPartialRows: true,
};
const schemas = {
...schemasDef,
@@ -219,17 +195,14 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
],
bucket: [0, 3],
};
- const actual = buildPipelineVisFunction.table(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.table(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
it('with showPartialRows=true and showMetricsAtAllLevels=false', () => {
- const visState = {
- ...visStateDef,
- params: {
- showMetricsAtAllLevels: false,
- showPartialRows: true,
- },
+ const params = {
+ showMetricsAtAllLevels: false,
+ showPartialRows: true,
};
const schemas = {
...schemasDef,
@@ -241,14 +214,14 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
],
bucket: [0, 3],
};
- const actual = buildPipelineVisFunction.table(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.table(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
});
describe('handles metric function', () => {
it('without buckets', () => {
- const visState = { ...visStateDef, params: { metric: {} } };
+ const params = { metric: {} };
const schemas = {
...schemasDef,
metric: [
@@ -256,12 +229,12 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
{ ...schemaConfig, accessor: 1 },
],
};
- const actual = buildPipelineVisFunction.metric(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.metric(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
it('with buckets', () => {
- const visState = { ...visStateDef, params: { metric: {} } };
+ const params = { metric: {} };
const schemas = {
...schemasDef,
metric: [
@@ -270,21 +243,21 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
],
group: [{ accessor: 2 }],
};
- const actual = buildPipelineVisFunction.metric(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.metric(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
it('with percentage mode should have percentage format', () => {
- const visState = { ...visStateDef, params: { metric: { percentageMode: true } } };
+ const params = { metric: { percentageMode: true } };
const schemas = { ...schemasDef };
- const actual = buildPipelineVisFunction.metric(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.metric(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
});
describe('handles tagcloud function', () => {
it('without buckets', () => {
- const actual = buildPipelineVisFunction.tagcloud(visStateDef, schemasDef, uiState);
+ const actual = buildPipelineVisFunction.tagcloud({}, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
@@ -293,21 +266,21 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
...schemasDef,
segment: [{ accessor: 1 }],
};
- const actual = buildPipelineVisFunction.tagcloud(visStateDef, schemas, uiState);
+ const actual = buildPipelineVisFunction.tagcloud({}, schemas, uiState);
expect(actual).toMatchSnapshot();
});
it('with boolean param showLabel', () => {
- const visState = { ...visStateDef, params: { showLabel: false } };
- const actual = buildPipelineVisFunction.tagcloud(visState, schemasDef, uiState);
+ const params = { showLabel: false };
+ const actual = buildPipelineVisFunction.tagcloud(params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
});
describe('handles region_map function', () => {
it('without buckets', () => {
- const visState = { ...visStateDef, params: { metric: {} } };
- const actual = buildPipelineVisFunction.region_map(visState, schemasDef, uiState);
+ const params = { metric: {} };
+ const actual = buildPipelineVisFunction.region_map(params, schemasDef, uiState);
expect(actual).toMatchSnapshot();
});
@@ -316,19 +289,19 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
...schemasDef,
segment: [1, 2],
};
- const actual = buildPipelineVisFunction.region_map(visStateDef, schemas, uiState);
+ const actual = buildPipelineVisFunction.region_map({}, schemas, uiState);
expect(actual).toMatchSnapshot();
});
});
it('handles tile_map function', () => {
- const visState = { ...visStateDef, params: { metric: {} } };
+ const params = { metric: {} };
const schemas = {
...schemasDef,
segment: [1, 2],
geo_centroid: [3, 4],
};
- const actual = buildPipelineVisFunction.tile_map(visState, schemas, uiState);
+ const actual = buildPipelineVisFunction.tile_map(params, schemas, uiState);
expect(actual).toMatchSnapshot();
});
@@ -337,7 +310,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
...schemasDef,
segment: [1, 2],
};
- const actual = buildPipelineVisFunction.pie(visStateDef, schemas, uiState);
+ const actual = buildPipelineVisFunction.pie({}, schemas, uiState);
expect(actual).toMatchSnapshot();
});
});
@@ -347,11 +320,16 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
it('calls toExpression on vis_type if it exists', async () => {
const vis = ({
- getCurrentState: () => {},
- getUiState: () => null,
+ getState: () => {},
isHierarchical: () => false,
- aggs: {
- getResponseAggs: () => [],
+ data: {
+ aggs: {
+ getResponseAggs: () => [],
+ },
+ searchSource: {
+ getField: jest.fn(),
+ getParent: jest.fn(),
+ },
},
// @ts-ignore
type: {
@@ -359,7 +337,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
},
} as unknown) as Vis;
const expression = await buildPipeline(vis, {
- searchSource: searchSourceMock,
timefilter: dataStart.query.timefilter.timefilter,
});
expect(expression).toMatchSnapshot();
@@ -370,7 +347,6 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
const dataStart = dataPluginMock.createStartContract();
let aggs: IAggConfig[];
- let visState: any;
let vis: Vis;
let params: any;
@@ -397,7 +373,11 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
describe('test y dimension format for histogram chart', () => {
beforeEach(() => {
- visState = {
+ vis = {
+ // @ts-ignore
+ type: {
+ name: 'histogram',
+ },
params: {
seriesParams: [
{
@@ -414,24 +394,16 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
},
],
},
- };
-
- vis = {
- // @ts-ignore
- type: {
- name: 'histogram',
- },
- aggs: {
- getResponseAggs: () => {
- return aggs;
- },
+ data: {
+ aggs: {
+ getResponseAggs: () => {
+ return aggs;
+ },
+ } as any,
},
isHierarchical: () => {
return false;
},
- getCurrentState: () => {
- return visState;
- },
};
});
@@ -443,7 +415,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
});
it('with one numeric metric in percentage mode', async () => {
- visState.params.valueAxes[0].scale.mode = 'percentage';
+ vis.params.valueAxes[0].scale.mode = 'percentage';
const dimensions = await buildVislibDimensions(vis, params);
const expected = { id: 'percent' };
const actual = dimensions.y[0].format;
@@ -454,33 +426,31 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
const aggConfig = aggs[0];
aggs = [{ ...aggConfig } as IAggConfig, { ...aggConfig, id: '5' } as IAggConfig];
- visState = {
- params: {
- seriesParams: [
- {
- data: { id: '0' },
- valueAxis: 'axis-y-1',
- },
- {
- data: { id: '5' },
- valueAxis: 'axis-y-2',
- },
- ],
- valueAxes: [
- {
- id: 'axis-y-1',
- scale: {
- mode: 'normal',
- },
+ vis.params = {
+ seriesParams: [
+ {
+ data: { id: '0' },
+ valueAxis: 'axis-y-1',
+ },
+ {
+ data: { id: '5' },
+ valueAxis: 'axis-y-2',
+ },
+ ],
+ valueAxes: [
+ {
+ id: 'axis-y-1',
+ scale: {
+ mode: 'normal',
},
- {
- id: 'axis-y-2',
- scale: {
- mode: 'percentage',
- },
+ },
+ {
+ id: 'axis-y-2',
+ scale: {
+ mode: 'percentage',
},
- ],
- },
+ },
+ ],
};
const dimensions = await buildVislibDimensions(vis, params);
@@ -493,29 +463,27 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
describe('test y dimension format for gauge chart', () => {
beforeEach(() => {
- visState = { params: { gauge: {} } };
-
vis = {
// @ts-ignore
type: {
name: 'gauge',
},
- aggs: {
- getResponseAggs: () => {
- return aggs;
- },
+ params: { gauge: {} },
+ data: {
+ aggs: {
+ getResponseAggs: () => {
+ return aggs;
+ },
+ } as any,
},
isHierarchical: () => {
return false;
},
- getCurrentState: () => {
- return visState;
- },
};
});
it('with percentageMode = false', async () => {
- visState.params.gauge.percentageMode = false;
+ vis.params.gauge.percentageMode = false;
const dimensions = await buildVislibDimensions(vis, params);
const expected = { id: 'number' };
const actual = dimensions.y[0].format;
@@ -523,7 +491,7 @@ describe('visualize loader pipeline helpers: build pipeline', () => {
});
it('with percentageMode = true', async () => {
- visState.params.gauge.percentageMode = true;
+ vis.params.gauge.percentageMode = true;
const dimensions = await buildVislibDimensions(vis, params);
const expected = { id: 'percent' };
const actual = dimensions.y[0].format;
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts
index de974e6e969ef..ea15cd9201fd7 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/build_pipeline.ts
@@ -21,14 +21,13 @@ import { get } from 'lodash';
import moment from 'moment';
import { SerializedFieldFormat } from '../../../../../../../plugins/expressions/public';
import {
- fieldFormats,
IAggConfig,
- ISearchSource,
+ fieldFormats,
search,
TimefilterContract,
} from '../../../../../../../plugins/data/public';
-const { isDateHistogramBucketAggConfig } = search.aggs;
import { Vis, VisParams } from '../types';
+const { isDateHistogramBucketAggConfig } = search.aggs;
interface SchemaConfigParams {
precision?: number;
@@ -59,7 +58,7 @@ export interface Schemas {
}
type buildVisFunction = (
- visState: ReturnType,
+ params: VisParams,
schemas: Schemas,
uiState: any,
meta?: { savedObjectId?: string }
@@ -139,7 +138,12 @@ const getSchemas = (
const schemas: Schemas = {
metric: [],
};
- const responseAggs = vis.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled);
+
+ if (!vis.data.aggs) {
+ return schemas;
+ }
+
+ const responseAggs = vis.data.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled);
const isHierarchical = vis.isHierarchical();
const metrics = responseAggs.filter((agg: IAggConfig) => agg.type.type === 'metrics');
responseAggs.forEach((agg: IAggConfig) => {
@@ -228,9 +232,8 @@ export const prepareDimension = (variable: string, data: any) => {
};
const adjustVislibDimensionFormmaters = (vis: Vis, dimensions: { y: any[] }): void => {
- const visState = vis.getCurrentState();
- const visConfig = visState.params;
- const responseAggs = vis.aggs.getResponseAggs().filter((agg: IAggConfig) => agg.enabled);
+ const visConfig = vis.params;
+ const responseAggs = vis.data.aggs!.getResponseAggs().filter((agg: IAggConfig) => agg.enabled);
(dimensions.y || []).forEach(yDimension => {
const yAgg = responseAggs[yDimension.accessor];
@@ -252,27 +255,26 @@ const adjustVislibDimensionFormmaters = (vis: Vis, dimensions: { y: any[] }): vo
};
export const buildPipelineVisFunction: BuildPipelineVisFunction = {
- vega: visState => {
- return `vega ${prepareString('spec', visState.params.spec)}`;
+ vega: params => {
+ return `vega ${prepareString('spec', params.spec)}`;
},
- input_control_vis: visState => {
- return `input_control_vis ${prepareJson('visConfig', visState.params)}`;
+ input_control_vis: params => {
+ return `input_control_vis ${prepareJson('visConfig', params)}`;
},
- metrics: (visState, schemas, uiState = {}, meta) => {
- const paramsJson = prepareJson('params', visState.params);
+ metrics: (params, schemas, uiState = {}) => {
+ const paramsJson = prepareJson('params', params);
const uiStateJson = prepareJson('uiState', uiState);
- const savedObjectIdParam = prepareString('savedObjectId', meta?.savedObjectId);
- const params = [paramsJson, uiStateJson, savedObjectIdParam].filter(param => Boolean(param));
- return `tsvb ${params.join(' ')}`;
+ const paramsArray = [paramsJson, uiStateJson].filter(param => Boolean(param));
+ return `tsvb ${paramsArray.join(' ')}`;
},
- timelion: visState => {
- const expression = prepareString('expression', visState.params.expression);
- const interval = prepareString('interval', visState.params.interval);
+ timelion: params => {
+ const expression = prepareString('expression', params.expression);
+ const interval = prepareString('interval', params.interval);
return `timelion_vis ${expression}${interval}`;
},
- markdown: visState => {
- const { markdown, fontSize, openLinksInNewTab } = visState.params;
+ markdown: params => {
+ const { markdown, fontSize, openLinksInNewTab } = params;
let escapedMarkdown = '';
if (typeof markdown === 'string' || markdown instanceof String) {
escapedMarkdown = escapeString(markdown.toString());
@@ -282,14 +284,14 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
expr += prepareValue('openLinksInNewTab', openLinksInNewTab);
return expr;
},
- table: (visState, schemas) => {
+ table: (params, schemas) => {
const visConfig = {
- ...visState.params,
- ...buildVisConfig.table(schemas, visState.params),
+ ...params,
+ ...buildVisConfig.table(schemas, params),
};
return `kibana_table ${prepareJson('visConfig', visConfig)}`;
},
- metric: (visState, schemas) => {
+ metric: (params, schemas) => {
const {
percentageMode,
useRanges,
@@ -299,11 +301,11 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
labels,
invertColors,
style,
- } = visState.params.metric;
+ } = params.metric;
const { metrics, bucket } = buildVisConfig.metric(schemas).dimensions;
// fix formatter for percentage mode
- if (get(visState.params, 'metric.percentageMode') === true) {
+ if (get(params, 'metric.percentageMode') === true) {
metrics.forEach((metric: SchemaConfig) => {
metric.format = { id: 'percent' };
});
@@ -335,8 +337,8 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
return expr;
},
- tagcloud: (visState, schemas) => {
- const { scale, orientation, minFontSize, maxFontSize, showLabel } = visState.params;
+ tagcloud: (params, schemas) => {
+ const { scale, orientation, minFontSize, maxFontSize, showLabel } = params;
const { metric, bucket } = buildVisConfig.tagcloud(schemas);
let expr = `tagcloud metric={visdimension ${metric.accessor}} `;
expr += prepareValue('scale', scale);
@@ -348,23 +350,23 @@ export const buildPipelineVisFunction: BuildPipelineVisFunction = {
return expr;
},
- region_map: (visState, schemas) => {
+ region_map: (params, schemas) => {
const visConfig = {
- ...visState.params,
+ ...params,
...buildVisConfig.region_map(schemas),
};
return `regionmap ${prepareJson('visConfig', visConfig)}`;
},
- tile_map: (visState, schemas) => {
+ tile_map: (params, schemas) => {
const visConfig = {
- ...visState.params,
+ ...params,
...buildVisConfig.tile_map(schemas),
};
return `tilemap ${prepareJson('visConfig', visConfig)}`;
},
- pie: (visState, schemas) => {
+ pie: (params, schemas) => {
const visConfig = {
- ...visState.params,
+ ...params,
...buildVisConfig.pie(schemas),
};
return `kibana_pie ${prepareJson('visConfig', visConfig)}`;
@@ -440,7 +442,6 @@ const buildVisConfig: BuildVisConfigFunction = {
export const buildVislibDimensions = async (
vis: any,
params: {
- searchSource: any;
timefilter: TimefilterContract;
timeRange?: any;
abortSignal?: AbortSignal;
@@ -460,7 +461,7 @@ export const buildVislibDimensions = async (
splitColumn: schemas.split_column,
};
if (schemas.segment) {
- const xAgg = vis.aggs.getResponseAggs()[dimensions.x.accessor];
+ const xAgg = vis.data.aggs.getResponseAggs()[dimensions.x.accessor];
if (xAgg.type.name === 'date_histogram') {
dimensions.x.params.date = true;
const { esUnit, esValue } = xAgg.buckets.getInterval();
@@ -472,7 +473,7 @@ export const buildVislibDimensions = async (
} else if (xAgg.type.name === 'histogram') {
const intervalParam = xAgg.type.paramByName('interval');
const output = { params: {} as any };
- await intervalParam.modifyAggConfigOnSearchRequestStart(xAgg, params.searchSource, {
+ await intervalParam.modifyAggConfigOnSearchRequestStart(xAgg, vis.data.searchSource, {
abortSignal: params.abortSignal,
});
intervalParam.write(xAgg, output);
@@ -487,18 +488,14 @@ export const buildVislibDimensions = async (
export const buildPipeline = async (
vis: Vis,
params: {
- searchSource: ISearchSource;
timefilter: TimefilterContract;
timeRange?: any;
- savedObjectId?: string;
}
) => {
- const { searchSource } = params;
- const { indexPattern } = vis;
- const query = searchSource.getField('query');
- const filters = searchSource.getField('filter');
- const visState = vis.getCurrentState();
- const uiState = vis.getUiState();
+ const { indexPattern, searchSource } = vis.data;
+ const query = searchSource!.getField('query');
+ const filters = searchSource!.getField('filter');
+ const { uiState } = vis;
// context
let pipeline = `kibana | kibana_context `;
@@ -508,18 +505,18 @@ export const buildPipeline = async (
if (filters) {
pipeline += prepareJson('filters', filters);
}
- if (vis.savedSearchId) {
- pipeline += prepareString('savedSearchId', vis.savedSearchId);
+ if (vis.data.savedSearchId) {
+ pipeline += prepareString('savedSearchId', vis.data.savedSearchId);
}
pipeline += '| ';
// request handler
if (vis.type.requestHandler === 'courier') {
pipeline += `esaggs
- ${prepareString('index', indexPattern.id)}
+ ${prepareString('index', indexPattern!.id)}
metricsAtAllLevels=${vis.isHierarchical()}
partialRows=${vis.type.requiresPartialRows || vis.params.showPartialRows || false}
- ${prepareJson('aggConfigs', visState.aggs)} | `;
+ ${prepareJson('aggConfigs', vis.data.aggs!.aggs)} | `;
}
const schemas = getSchemas(vis, {
@@ -527,18 +524,16 @@ export const buildPipeline = async (
timefilter: params.timefilter,
});
if (buildPipelineVisFunction[vis.type.name]) {
- pipeline += buildPipelineVisFunction[vis.type.name](visState, schemas, uiState, {
- savedObjectId: params.savedObjectId,
- });
+ pipeline += buildPipelineVisFunction[vis.type.name](vis.params, schemas, uiState);
} else if (vislibCharts.includes(vis.type.name)) {
- const visConfig = visState.params;
+ const visConfig = { ...vis.params };
visConfig.dimensions = await buildVislibDimensions(vis, params);
- pipeline += `vislib type='${vis.type.name}' ${prepareJson('visConfig', visState.params)}`;
+ pipeline += `vislib type='${vis.type.name}' ${prepareJson('visConfig', visConfig)}`;
} else if (vis.type.toExpression) {
pipeline += await vis.type.toExpression(vis, params);
} else {
- const visConfig = visState.params;
+ const visConfig = { ...vis.params };
visConfig.dimensions = schemas;
pipeline += `visualization type='${vis.type.name}'
${prepareJson('visConfig', visConfig)}
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/update_status.test.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/update_status.test.js
deleted file mode 100644
index c63a8cd48e625..0000000000000
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/update_status.test.js
+++ /dev/null
@@ -1,102 +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 { getUpdateStatus, Status } from './update_status';
-
-// Parts of the tests in this file are generated more dynamically, based on the
-// values inside the Status object.Make sure this object has one function per entry
-// in Status, that actually change on the passed $scope, what needs to be changed
-// so that we expect the getUpdateStatus function to actually detect a change.
-const changeFunctions = {
- [Status.AGGS]: $scope => ($scope.vis.aggs = { foo: 'new' }),
- [Status.DATA]: $scope => ($scope.visData = { foo: 'new' }),
- [Status.PARAMS]: $scope => ($scope.vis.params = { foo: 'new' }),
- [Status.RESIZE]: $scope => ($scope.vis.size = [50, 50]),
- [Status.TIME]: $scope => ($scope.vis.filters.timeRange = { from: 'now-7d', to: 'now' }),
- [Status.UI_STATE]: $scope => ($scope.uiState = { foo: 'new' }),
-};
-
-describe('getUpdateStatus', () => {
- function getScope() {
- return {
- vis: {
- aggs: {},
- size: [100, 100],
- params: {},
- filters: {},
- },
- uiState: {},
- visData: {},
- };
- }
-
- function initStatusCheckerAndChangeProperty(type, requiresUpdateStatus) {
- const $scope = getScope();
- // Call the getUpdateStatus function initially, so it can store it's current state
- getUpdateStatus(requiresUpdateStatus, $scope, $scope);
-
- // Get the change function for that specific change type
- const changeFn = changeFunctions[type];
- if (!changeFn) {
- throw new Error(`Please implement the test change function for ${type}.`);
- }
-
- // Call that change function to manipulate the scope so it changed.
- changeFn($scope);
-
- return getUpdateStatus(requiresUpdateStatus, $scope, $scope);
- }
-
- it('should be a function', () => {
- expect(typeof getUpdateStatus).toBe('function');
- });
-
- Object.entries(Status).forEach(([typeKey, typeValue]) => {
- // This block automatically creates very simple tests for each of the Status
- // keys, so we have simple tests per changed property.
- // If it makes sense to test more specific behavior of a specific change detection
- // please add additional tests for that.
-
- it(`should detect changes for Status.${typeKey}`, () => {
- // Check whether the required change type is not correctly determined
- const status = initStatusCheckerAndChangeProperty(typeValue, [typeValue]);
- expect(status[typeValue]).toBe(true);
- });
-
- it(`should not detect changes in other properties when changing Status.${typeKey}`, () => {
- // Only change typeKey, but track changes for all status changes
- const status = initStatusCheckerAndChangeProperty(typeValue, Object.values(Status));
- Object.values(Status)
- // Filter out the actual changed property so we only test for all other properties
- .filter(stat => stat !== typeValue)
- .forEach(otherProp => {
- expect(status[otherProp]).toBeFalsy();
- });
- });
-
- it(`should not detect changes if not requested for Status.${typeKey}`, () => {
- const allOtherStatusProperties = Object.values(Status).filter(stat => stat !== typeValue);
- // Change only the typeKey property, but do not listen for changes on it
- // listen on all other status changes instead.
- const status = initStatusCheckerAndChangeProperty(typeValue, allOtherStatusProperties);
- // The typeValue check should be falsy, since we did not request tracking it.
- expect(status[typeValue]).toBeFalsy();
- });
- });
-});
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/update_status.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/update_status.ts
deleted file mode 100644
index 92a9ce8366f4f..0000000000000
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/legacy/update_status.ts
+++ /dev/null
@@ -1,117 +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 { PersistedState } from '../../../../../../../plugins/visualizations/public';
-import { calculateObjectHash } from '../../../../../../../plugins/kibana_utils/common';
-import { Vis } from '../vis';
-
-enum Status {
- AGGS = 'aggs',
- DATA = 'data',
- PARAMS = 'params',
- RESIZE = 'resize',
- TIME = 'time',
- UI_STATE = 'uiState',
-}
-
-/**
- * Checks whether the hash of a specific key in the given oldStatus has changed
- * compared to the new valueHash passed.
- */
-function hasHashChanged(
- valueHash: string,
- oldStatus: { [key in T]?: string },
- name: T
-): boolean {
- const oldHash = oldStatus[name];
- return oldHash !== valueHash;
-}
-
-interface Size {
- width: number;
- height: number;
-}
-
-function hasSizeChanged(size: Size, oldSize?: Size): boolean {
- if (!oldSize) {
- return true;
- }
- return oldSize.width !== size.width || oldSize.height !== size.height;
-}
-
-function getUpdateStatus(
- requiresUpdateStatus: T[] = [],
- obj: any,
- param: { vis: Vis; visData: any; uiState: PersistedState }
-): { [reqStats in T]: boolean } {
- const status = {} as { [reqStats in Status]: boolean };
-
- // If the vis type doesn't need update status, skip all calculations
- if (requiresUpdateStatus.length === 0) {
- return status;
- }
-
- if (!obj._oldStatus) {
- obj._oldStatus = {};
- }
-
- for (const requiredStatus of requiresUpdateStatus) {
- let hash;
- // Calculate all required status updates for this visualization
- switch (requiredStatus) {
- case Status.AGGS:
- hash = calculateObjectHash(param.vis.aggs);
- status.aggs = hasHashChanged(hash, obj._oldStatus, 'aggs');
- obj._oldStatus.aggs = hash;
- break;
- case Status.DATA:
- hash = calculateObjectHash(param.visData);
- status.data = hasHashChanged(hash, obj._oldStatus, 'data');
- obj._oldStatus.data = hash;
- break;
- case Status.PARAMS:
- hash = calculateObjectHash(param.vis.params);
- status.params = hasHashChanged(hash, obj._oldStatus, 'param');
- obj._oldStatus.param = hash;
- break;
- case Status.RESIZE:
- const width: number = param.vis.size ? param.vis.size[0] : 0;
- const height: number = param.vis.size ? param.vis.size[1] : 0;
- const size = { width, height };
- status.resize = hasSizeChanged(size, obj._oldStatus.resize);
- obj._oldStatus.resize = size;
- break;
- case Status.TIME:
- const timeRange = param.vis.filters && param.vis.filters.timeRange;
- hash = calculateObjectHash(timeRange);
- status.time = hasHashChanged(hash, obj._oldStatus, 'time');
- obj._oldStatus.time = hash;
- break;
- case Status.UI_STATE:
- hash = calculateObjectHash(param.uiState);
- status.uiState = hasHashChanged(hash, obj._oldStatus, 'uiState');
- obj._oldStatus.uiState = hash;
- break;
- }
- }
-
- return status;
-}
-
-export { getUpdateStatus, Status };
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
index 4ee727e46f4d6..dcd11c920f17c 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/mocks.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { PluginInitializerContext } from '../../../../../../core/public';
+import { CoreSetup, PluginInitializerContext } from '../../../../../../core/public';
import { VisualizationsSetup, VisualizationsStart } from './';
import { VisualizationsPlugin } from './plugin';
import { coreMock } from '../../../../../../core/public/mocks';
@@ -26,6 +26,7 @@ import { expressionsPluginMock } from '../../../../../../plugins/expressions/pub
import { dataPluginMock } from '../../../../../../plugins/data/public/mocks';
import { usageCollectionPluginMock } from '../../../../../../plugins/usage_collection/public/mocks';
import { uiActionsPluginMock } from '../../../../../../plugins/ui_actions/public/mocks';
+import { VisualizationsStartDeps } from './plugin';
const createSetupContract = (): VisualizationsSetup => ({
createBaseVisualization: jest.fn(),
@@ -41,12 +42,14 @@ const createStartContract = (): VisualizationsStart => ({
savedVisualizationsLoader: {} as any,
showNewVisModal: jest.fn(),
createVis: jest.fn(),
+ convertFromSerializedVis: jest.fn(),
+ convertToSerializedVis: jest.fn(),
});
const createInstance = async () => {
const plugin = new VisualizationsPlugin({} as PluginInitializerContext);
- const setup = plugin.setup(coreMock.createSetup(), {
+ const setup = plugin.setup(coreMock.createSetup() as CoreSetup, {
data: dataPluginMock.createSetupContract(),
expressions: expressionsPluginMock.createSetupContract(),
embeddable: embeddablePluginMock.createSetupContract(),
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
index 953caecefb974..c826841e2bcf3 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/plugin.ts
@@ -39,6 +39,8 @@ import {
setSavedVisualizationsLoader,
setTimeFilter,
setAggs,
+ setChrome,
+ setOverlays,
} from './services';
import { VISUALIZE_EMBEDDABLE_TYPE, VisualizeEmbeddableFactory } from './embeddable';
import { ExpressionsSetup, ExpressionsStart } from '../../../../../../plugins/expressions/public';
@@ -48,14 +50,16 @@ import { visualization as visualizationRenderer } from './expressions/visualizat
import {
DataPublicPluginSetup,
DataPublicPluginStart,
- IIndexPattern,
} from '../../../../../../plugins/data/public';
import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/public';
import { createSavedVisLoader, SavedVisualizationsLoader } from './saved_visualizations';
-import { VisImpl } from './vis_impl';
+import { SerializedVis, Vis } from './vis';
import { showNewVisModal } from './wizard';
import { UiActionsStart } from '../../../../../../plugins/ui_actions/public';
-import { VisState } from './types';
+import {
+ convertFromSerializedVis,
+ convertToSerializedVis,
+} from './saved_visualizations/_saved_vis';
/**
* Interface for this plugin's returned setup/start contracts.
@@ -67,7 +71,9 @@ export type VisualizationsSetup = TypesSetup;
export interface VisualizationsStart extends TypesStart {
savedVisualizationsLoader: SavedVisualizationsLoader;
- createVis: (indexPattern: IIndexPattern, visState?: VisState) => VisImpl;
+ createVis: (visType: string, visState?: SerializedVis) => Vis;
+ convertToSerializedVis: typeof convertToSerializedVis;
+ convertFromSerializedVis: typeof convertFromSerializedVis;
showNewVisModal: typeof showNewVisModal;
}
@@ -105,7 +111,7 @@ export class VisualizationsPlugin
constructor(initializerContext: PluginInitializerContext) {}
public setup(
- core: CoreSetup,
+ core: CoreSetup,
{ expressions, embeddable, usageCollection, data }: VisualizationsSetupDeps
): VisualizationsSetup {
setUISettings(core.uiSettings);
@@ -114,7 +120,9 @@ export class VisualizationsPlugin
expressions.registerFunction(visualizationFunction);
expressions.registerRenderer(visualizationRenderer);
- const embeddableFactory = new VisualizeEmbeddableFactory();
+ const embeddableFactory = new VisualizeEmbeddableFactory(
+ async () => (await core.getStartServices())[1].uiActions
+ );
embeddable.registerEmbeddableFactory(VISUALIZE_EMBEDDABLE_TYPE, embeddableFactory);
return {
@@ -138,6 +146,8 @@ export class VisualizationsPlugin
setUiActions(uiActions);
setTimeFilter(data.query.timefilter.timefilter);
setAggs(data.search.aggs);
+ setOverlays(core.overlays);
+ setChrome(core.chrome);
const savedVisualizationsLoader = createSavedVisLoader({
savedObjectsClient: core.savedObjects.client,
indexPatterns: data.indexPatterns,
@@ -155,8 +165,9 @@ export class VisualizationsPlugin
* @param {IIndexPattern} indexPattern - index pattern to use
* @param {VisState} visState - visualization configuration
*/
- createVis: (indexPattern: IIndexPattern, visState?: VisState) =>
- new VisImpl(indexPattern, visState),
+ createVis: (visType: string, visState?: SerializedVis) => new Vis(visType, visState),
+ convertToSerializedVis,
+ convertFromSerializedVis,
savedVisualizationsLoader,
};
}
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts
index e381a01edef8b..c9906428ccb31 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/_saved_vis.ts
@@ -32,65 +32,74 @@ import {
// @ts-ignore
import { updateOldState } from '../legacy/vis_update_state';
import { extractReferences, injectReferences } from './saved_visualization_references';
-import { IIndexPattern } from '../../../../../../../plugins/data/public';
-import { VisSavedObject } from '../types';
-import { VisImpl } from '../vis_impl';
+import {
+ IIndexPattern,
+ ISearchSource,
+ SearchSource,
+} from '../../../../../../../plugins/data/public';
+import { ISavedVis, SerializedVis } from '../types';
import { createSavedSearchesLoader } from '../../../../../../../plugins/discover/public';
-
-async function _afterEsResp(savedVis: VisSavedObject, services: any) {
- await _getLinkedSavedSearch(savedVis, services);
- savedVis.searchSource!.setField('size', 0);
- savedVis.vis = savedVis.vis ? _updateVis(savedVis) : await _createVis(savedVis);
- return savedVis;
-}
-
-async function _getLinkedSavedSearch(savedVis: VisSavedObject, services: any) {
- const linkedSearch = !!savedVis.savedSearchId;
- const current = savedVis.savedSearch;
-
- if (linkedSearch && current && current.id === savedVis.savedSearchId) {
- return;
- }
-
- if (savedVis.savedSearch) {
- savedVis.searchSource!.setParent(savedVis.savedSearch.searchSource.getParent());
- savedVis.savedSearch.destroy();
- delete savedVis.savedSearch;
- }
- const savedSearches = createSavedSearchesLoader(services);
-
- if (linkedSearch) {
- savedVis.savedSearch = await savedSearches.get(savedVis.savedSearchId!);
- savedVis.searchSource!.setParent(savedVis.savedSearch!.searchSource);
- }
-}
-
-async function _createVis(savedVis: VisSavedObject) {
- savedVis.visState = updateOldState(savedVis.visState);
-
- // visState doesn't yet exist when importing a visualization, so we can't
- // assume that exists at this point. If it does exist, then we're not
- // importing a visualization, so we want to sync the title.
- if (savedVis.visState) {
- savedVis.visState.title = savedVis.title;
- }
-
- savedVis.vis = new VisImpl(savedVis.searchSource!.getField('index')!, savedVis.visState);
-
- savedVis.vis!.savedSearchId = savedVis.savedSearchId;
-
- return savedVis.vis;
-}
-
-function _updateVis(savedVis: VisSavedObject) {
- if (savedVis.vis && savedVis.searchSource) {
- savedVis.vis.indexPattern = savedVis.searchSource.getField('index');
- savedVis.visState.title = savedVis.title;
- savedVis.vis.setState(savedVis.visState);
- savedVis.vis.savedSearchId = savedVis.savedSearchId;
+import { getChrome, getOverlays, getIndexPatterns, getSavedObjects } from '../services';
+
+export const convertToSerializedVis = async (savedVis: ISavedVis): Promise => {
+ const { visState } = savedVis;
+ const searchSource =
+ savedVis.searchSource && (await getSearchSource(savedVis.searchSource, savedVis.savedSearchId));
+
+ const indexPattern =
+ searchSource && searchSource.getField('index') ? searchSource.getField('index')!.id : undefined;
+
+ const aggs = indexPattern ? visState.aggs || [] : visState.aggs;
+
+ return {
+ id: savedVis.id,
+ title: savedVis.title,
+ type: visState.type,
+ description: savedVis.description,
+ params: visState.params,
+ uiState: JSON.parse(savedVis.uiStateJSON || '{}'),
+ data: {
+ indexPattern,
+ aggs,
+ searchSource,
+ savedSearchId: savedVis.savedSearchId,
+ },
+ };
+};
+
+export const convertFromSerializedVis = (vis: SerializedVis): ISavedVis => {
+ return {
+ id: vis.id,
+ title: vis.title,
+ description: vis.description,
+ visState: {
+ type: vis.type,
+ aggs: vis.data.aggs,
+ params: vis.params,
+ },
+ uiStateJSON: JSON.stringify(vis.uiState),
+ searchSource: vis.data.searchSource!,
+ savedSearchId: vis.data.savedSearchId,
+ };
+};
+
+const getSearchSource = async (inputSearchSource: ISearchSource, savedSearchId?: string) => {
+ const searchSource = inputSearchSource.createCopy
+ ? inputSearchSource.createCopy()
+ : new SearchSource({ ...(inputSearchSource as any).fields });
+ if (savedSearchId) {
+ const savedSearch = await createSavedSearchesLoader({
+ savedObjectsClient: getSavedObjects().client,
+ indexPatterns: getIndexPatterns(),
+ chrome: getChrome(),
+ overlays: getOverlays(),
+ }).get(savedSearchId);
+
+ searchSource.setParent(savedSearch.searchSource);
}
- return savedVis.vis;
-}
+ searchSource!.setField('size', 0);
+ return searchSource;
+};
export function createSavedVisClass(services: SavedObjectKibanaServices) {
const SavedObjectClass = createSavedObjectClass(services);
@@ -131,8 +140,16 @@ export function createSavedVisClass(services: SavedObjectKibanaServices) {
savedSearchId: opts.savedSearchId,
version: 1,
},
- afterESResp: (savedObject: SavedObject) => {
- return _afterEsResp(savedObject as VisSavedObject, services) as Promise;
+ afterESResp: async (savedObject: SavedObject) => {
+ const savedVis = (savedObject as any) as ISavedVis;
+ savedVis.visState = await updateOldState(savedVis.visState);
+ if (savedVis.savedSearchId && savedVis.searchSource) {
+ savedObject.searchSource = await getSearchSource(
+ savedVis.searchSource,
+ savedVis.savedSearchId
+ );
+ }
+ return (savedVis as any) as SavedObject;
},
});
this.showInRecentlyAccessed = true;
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts
index 98af6d99025c2..2e3a4f0f58b27 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/saved_visualizations/saved_visualization_references.test.ts
@@ -18,7 +18,7 @@
*/
import { extractReferences, injectReferences } from './saved_visualization_references';
-import { VisSavedObject, VisState } from '../types';
+import { VisSavedObject, SavedVisState } from '../types';
describe('extractReferences', () => {
test('extracts nothing if savedSearchId is empty', () => {
@@ -140,7 +140,7 @@ Object {
},
],
},
- } as unknown) as VisState,
+ } as unknown) as SavedVisState,
} as VisSavedObject;
const references = [
{
@@ -201,7 +201,7 @@ Object {
},
],
},
- } as unknown) as VisState,
+ } as unknown) as SavedVisState,
} as VisSavedObject;
expect(() => injectReferences(context, [])).toThrowErrorMatchingInlineSnapshot(
`"Could not find index pattern reference \\"control_0_index_pattern\\""`
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts
index b2eebe8b5b57d..23cdeae7d15ff 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/services.ts
@@ -18,10 +18,13 @@
*/
import {
+ ApplicationStart,
Capabilities,
+ ChromeStart,
HttpStart,
I18nStart,
IUiSettingsClient,
+ OverlayStart,
SavedObjectsStart,
} from '../../../../../../core/public';
import { TypesStart } from './vis_types';
@@ -76,3 +79,9 @@ export const [getSavedVisualizationsLoader, setSavedVisualizationsLoader] = crea
export const [getAggs, setAggs] = createGetterSetter(
'AggConfigs'
);
+
+export const [getOverlays, setOverlays] = createGetterSetter('Overlays');
+
+export const [getChrome, setChrome] = createGetterSetter('Chrome');
+
+export const [getApplication, setApplication] = createGetterSetter('Application');
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts
index d8e3ccdeb065e..8f93a179af3bc 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/types.ts
@@ -18,21 +18,33 @@
*/
import { SavedObject } from '../../../../../../plugins/saved_objects/public';
-import { Vis, VisState, VisParams, VisualizationController } from './vis';
-import { ISearchSource } from '../../../../../../plugins/data/public/';
-import { SavedSearch } from '../../../../../../plugins/discover/public';
+import { ISearchSource, AggConfigOptions } from '../../../../../../plugins/data/public';
+import { SerializedVis, Vis, VisParams } from './vis';
-export { Vis, VisState, VisParams, VisualizationController };
+export { Vis, SerializedVis, VisParams };
-export interface VisSavedObject extends SavedObject {
- vis: Vis;
- description?: string;
- searchSource: ISearchSource;
+export interface VisualizationController {
+ render(visData: any, visParams: any): Promise;
+ destroy(): void;
+ isLoaded?(): Promise | void;
+}
+
+export interface SavedVisState {
+ type: string;
+ params: VisParams;
+ aggs: AggConfigOptions[];
+}
+
+export interface ISavedVis {
+ id: string;
title: string;
+ description?: string;
+ visState: SavedVisState;
+ searchSource?: ISearchSource;
uiStateJSON?: string;
- destroy: () => void;
savedSearchRefName?: string;
savedSearchId?: string;
- savedSearch?: SavedSearch;
- visState: VisState;
}
+
+// @ts-ignore-next-line
+export interface VisSavedObject extends SavedObject, ISavedVis {}
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts
index eb262966a4a22..0ba936c9f6567 100644
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts
+++ b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis.ts
@@ -17,47 +17,182 @@
* under the License.
*/
+/**
+ * @name Vis
+ *
+ * @description This class consists of aggs, params, listeners, title, and type.
+ * - Aggs: Instances of IAggConfig.
+ * - Params: The settings in the Options tab.
+ *
+ * Not to be confused with vislib/vis.js.
+ */
+
+import { isFunction, defaults, cloneDeep } from 'lodash';
+import { PersistedState } from '../../../../../../../src/plugins/visualizations/public';
+// @ts-ignore
+import { updateVisualizationConfig } from './legacy/vis_update';
+import { getTypes, getAggs } from './services';
import { VisType } from './vis_types';
-import { Status } from './legacy/update_status';
-import { IAggConfigs } from '../../../../../../plugins/data/public';
-
-export interface Vis {
- type: VisType;
- getCurrentState: (
- includeDisabled?: boolean
- ) => {
- title: string;
- type: string;
- params: VisParams;
- aggs: Array<{ [key: string]: any }>;
- };
-
- /**
- * If a visualization based on the saved search,
- * the id is necessary for building an expression function in src/plugins/expressions/common/expression_functions/specs/kibana_context.ts
- */
+import {
+ IAggConfigs,
+ IndexPattern,
+ ISearchSource,
+ AggConfigOptions,
+} from '../../../../../../plugins/data/public';
+
+export interface SerializedVisData {
+ expression?: string;
+ aggs: AggConfigOptions[];
+ indexPattern?: string;
+ searchSource?: ISearchSource;
savedSearchId?: string;
+}
- // Since we haven't typed everything here yet, we basically "any" the rest
- // of that interface. This should be removed as soon as this type definition
- // has been completed. But that way we at least have typing for a couple of
- // properties on that type.
- [key: string]: any;
+export interface SerializedVis {
+ id: string;
+ title: string;
+ description?: string;
+ type: string;
+ params: VisParams;
+ uiState?: any;
+ data: SerializedVisData;
+}
+
+export interface VisData {
+ ast?: string;
+ aggs?: IAggConfigs;
+ indexPattern?: IndexPattern;
+ searchSource?: ISearchSource;
+ savedSearchId?: string;
}
export interface VisParams {
[key: string]: any;
}
-export interface VisState {
- title: string;
- type: VisType;
- params: VisParams;
- aggs: IAggConfigs;
-}
+export class Vis {
+ public readonly type: VisType;
+ public readonly id: string;
+ public title: string = '';
+ public description: string = '';
+ public params: VisParams = {};
+ // Session state is for storing information that is transitory, and will not be saved with the visualization.
+ // For instance, map bounds, which depends on the view port, browser window size, etc.
+ public sessionState: Record = {};
+ public data: VisData = {};
+
+ public readonly uiState: PersistedState;
+
+ constructor(visType: string, visState: SerializedVis = {} as any) {
+ this.type = this.getType(visType);
+ this.params = this.getParams(visState.params);
+ this.uiState = new PersistedState(visState.uiState);
+ this.id = visState.id;
+
+ this.setState(visState || {});
+ }
+
+ private getType(visType: string) {
+ const type = getTypes().get(visType);
+ if (!type) {
+ throw new Error(`Invalid type "${visType}"`);
+ }
+ return type;
+ }
+
+ private getParams(params: VisParams) {
+ return defaults({}, cloneDeep(params || {}), cloneDeep(this.type.visConfig.defaults || {}));
+ }
+
+ setState(state: SerializedVis) {
+ let typeChanged = false;
+ if (state.type && this.type.name !== state.type) {
+ // @ts-ignore
+ this.type = this.getType(state.type);
+ typeChanged = true;
+ }
+ if (state.title !== undefined) {
+ this.title = state.title;
+ }
+ if (state.description !== undefined) {
+ this.description = state.description;
+ }
+ if (state.params || typeChanged) {
+ this.params = this.getParams(state.params);
+ }
+
+ // move to migration script
+ updateVisualizationConfig(state.params, this.params);
+
+ if (state.data && state.data.searchSource) {
+ this.data.searchSource = state.data.searchSource!;
+ this.data.indexPattern = this.data.searchSource.getField('index');
+ }
+ if (state.data && state.data.savedSearchId) {
+ this.data.savedSearchId = state.data.savedSearchId;
+ }
+ if (state.data && state.data.aggs) {
+ let configStates = state.data.aggs;
+ configStates = this.initializeDefaultsFromSchemas(configStates, this.type.schemas.all || []);
+ if (!this.data.indexPattern) {
+ if (state.data.aggs.length) {
+ throw new Error('trying to initialize aggs without index pattern');
+ }
+ return;
+ }
+ this.data.aggs = getAggs().createAggConfigs(this.data.indexPattern, configStates);
+ }
+ }
+
+ clone() {
+ return new Vis(this.type.name, this.serialize());
+ }
+
+ serialize(): SerializedVis {
+ const aggs = this.data.aggs ? this.data.aggs.aggs.map(agg => agg.toJSON()) : [];
+ const indexPattern = this.data.searchSource && this.data.searchSource.getField('index');
+ return {
+ id: this.id,
+ title: this.title,
+ type: this.type.name,
+ params: cloneDeep(this.params) as any,
+ uiState: this.uiState.toJSON(),
+ data: {
+ aggs: aggs as any,
+ indexPattern: indexPattern ? indexPattern.id : undefined,
+ searchSource: this.data.searchSource!.createCopy(),
+ savedSearchId: this.data.savedSearchId,
+ },
+ };
+ }
+
+ toAST() {
+ return this.type.toAST(this.params);
+ }
+
+ // deprecated
+ isHierarchical() {
+ if (isFunction(this.type.hierarchicalData)) {
+ return !!this.type.hierarchicalData(this);
+ } else {
+ return !!this.type.hierarchicalData;
+ }
+ }
-export interface VisualizationController {
- render(visData: any, visParams: any, update: { [key in Status]: boolean }): Promise;
- destroy(): void;
- isLoaded?(): Promise | void;
+ private initializeDefaultsFromSchemas(configStates: AggConfigOptions[], schemas: any) {
+ // Set the defaults for any schema which has them. If the defaults
+ // for some reason has more then the max only set the max number
+ // of defaults (not sure why a someone define more...
+ // but whatever). Also if a schema.name is already set then don't
+ // set anything.
+ const newConfigs = [...configStates];
+ schemas
+ .filter((schema: any) => Array.isArray(schema.defaults) && schema.defaults.length > 0)
+ .filter((schema: any) => !configStates.find(agg => agg.schema && agg.schema === schema.name))
+ .forEach((schema: any) => {
+ const defaultSchemaConfig = schema.defaults.slice(0, schema.max);
+ defaultSchemaConfig.forEach((d: any) => newConfigs.push(d));
+ });
+ return newConfigs;
+ }
}
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.ts
deleted file mode 100644
index 0e759c3d9872c..0000000000000
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.d.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 { Vis, VisState, VisParams } from './vis';
-import { VisType } from './vis_types';
-import { IAggConfig, IIndexPattern } from '../../../../../../plugins/data/public';
-import { Schema } from '../../../../vis_default_editor/public';
-
-type InitVisStateType =
- | Partial
- | Partial & { type: string }>
- | string;
-
-export type VisImplConstructor = new (
- indexPattern: IIndexPattern,
- visState?: InitVisStateType
-) => VisImpl;
-
-export declare class VisImpl implements Vis {
- constructor(indexPattern: IIndexPattern, visState?: InitVisStateType);
-
- type: VisType;
- getCurrentState: (
- includeDisabled?: boolean
- ) => {
- title: string;
- type: string;
- params: VisParams;
- aggs: Array<{ [key: string]: any }>;
- };
-
- private initializeDefaultsFromSchemas(configStates: IAggConfig[], schemas: Schema[]);
-
- // Since we haven't typed everything here yet, we basically "any" the rest
- // of that interface. This should be removed as soon as this type definition
- // has been completed. But that way we at least have typing for a couple of
- // properties on that type.
- [key: string]: any;
-}
diff --git a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.js b/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.js
deleted file mode 100644
index abd8f351ae94d..0000000000000
--- a/src/legacy/core_plugins/visualizations/public/np_ready/public/vis_impl.js
+++ /dev/null
@@ -1,223 +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.
- */
-
-/**
- * @name Vis
- *
- * @description This class consists of aggs, params, listeners, title, and type.
- * - Aggs: Instances of IAggConfig.
- * - Params: The settings in the Options tab.
- *
- * Not to be confused with vislib/vis.js.
- */
-
-import { EventEmitter } from 'events';
-import _ from 'lodash';
-import { PersistedState } from '../../../../../../../src/plugins/visualizations/public';
-import { updateVisualizationConfig } from './legacy/vis_update';
-import { getTypes, getAggs } from './services';
-
-class VisImpl extends EventEmitter {
- constructor(indexPattern, visState) {
- super();
- visState = visState || {};
-
- if (_.isString(visState)) {
- visState = {
- type: visState,
- };
- }
-
- this.indexPattern = indexPattern;
- this._setUiState(new PersistedState());
- this.setCurrentState(visState);
- this.setState(this.getCurrentState(), false);
-
- // Session state is for storing information that is transitory, and will not be saved with the visualization.
- // For instance, map bounds, which depends on the view port, browser window size, etc.
- this.sessionState = {};
-
- this.API = {
- events: {
- filter: data => this.eventsSubject.next({ name: 'filterBucket', data }),
- brush: data => this.eventsSubject.next({ name: 'brush', data }),
- },
- };
- }
-
- initializeDefaultsFromSchemas(configStates, schemas) {
- // Set the defaults for any schema which has them. If the defaults
- // for some reason has more then the max only set the max number
- // of defaults (not sure why a someone define more...
- // but whatever). Also if a schema.name is already set then don't
- // set anything.
- const newConfigs = [...configStates];
- schemas
- .filter(schema => Array.isArray(schema.defaults) && schema.defaults.length > 0)
- .filter(schema => !configStates.find(agg => agg.schema && agg.schema === schema.name))
- .forEach(schema => {
- const defaults = schema.defaults.slice(0, schema.max);
- defaults.forEach(d => newConfigs.push(d));
- });
- return newConfigs;
- }
-
- setCurrentState(state) {
- this.title = state.title || '';
- const type = state.type || this.type;
- if (_.isString(type)) {
- this.type = getTypes().get(type);
- if (!this.type) {
- throw new Error(`Invalid type "${type}"`);
- }
- } else {
- this.type = type;
- }
-
- this.params = _.defaults(
- {},
- _.cloneDeep(state.params || {}),
- _.cloneDeep(this.type.visConfig.defaults || {})
- );
-
- updateVisualizationConfig(state.params, this.params);
-
- if (state.aggs || !this.aggs) {
- let configStates = state.aggs ? state.aggs.aggs || state.aggs : [];
- configStates = this.initializeDefaultsFromSchemas(configStates, this.type.schemas.all || []);
- this.aggs = getAggs().createAggConfigs(this.indexPattern, configStates);
- }
- }
-
- setState(state, updateCurrentState = true) {
- this._state = _.cloneDeep(state);
- if (updateCurrentState) {
- this.setCurrentState(this._state);
- }
- }
-
- setVisType(type) {
- this.type.type = type;
- }
-
- updateState() {
- this.setState(this.getCurrentState(true));
- this.emit('update');
- }
-
- forceReload() {
- this.emit('reload');
- }
-
- getCurrentState(includeDisabled) {
- return {
- title: this.title,
- type: this.type.name,
- params: _.cloneDeep(this.params),
- aggs: this.aggs.aggs
- .map(agg => agg.toJSON())
- .filter(agg => includeDisabled || agg.enabled)
- .filter(Boolean),
- };
- }
-
- copyCurrentState(includeDisabled = false) {
- const state = this.getCurrentState(includeDisabled);
- state.aggs = getAggs().createAggConfigs(
- this.indexPattern,
- state.aggs.aggs || state.aggs,
- this.type.schemas.all
- );
- return state;
- }
-
- getStateInternal(includeDisabled) {
- return {
- title: this._state.title,
- type: this._state.type,
- params: this._state.params,
- aggs: this._state.aggs.filter(agg => includeDisabled || agg.enabled),
- };
- }
-
- getEnabledState() {
- return this.getStateInternal(false);
- }
-
- getAggConfig() {
- return this.aggs.clone({ enabledOnly: true });
- }
-
- getState() {
- return this.getStateInternal(true);
- }
-
- isHierarchical() {
- if (_.isFunction(this.type.hierarchicalData)) {
- return !!this.type.hierarchicalData(this);
- } else {
- return !!this.type.hierarchicalData;
- }
- }
-
- hasSchemaAgg(schemaName, aggTypeName) {
- const aggs = this.aggs.bySchemaName(schemaName) || [];
- return aggs.some(function(agg) {
- if (!agg.type || !agg.type.name) return false;
- return agg.type.name === aggTypeName;
- });
- }
-
- hasUiState() {
- return !!this.__uiState;
- }
-
- /***
- * this should not be used outside of visualize
- * @param uiState
- * @private
- */
- _setUiState(uiState) {
- if (uiState instanceof PersistedState) {
- this.__uiState = uiState;
- }
- }
-
- getUiState() {
- return this.__uiState;
- }
-
- /**
- * Currently this is only used to extract map-specific information
- * (e.g. mapZoom, mapCenter).
- */
- uiStateVal(key, val) {
- if (this.hasUiState()) {
- if (_.isUndefined(val)) {
- return this.__uiState.get(key);
- }
- return this.__uiState.set(key, val);
- }
- return val;
- }
-}
-
-VisImpl.prototype.type = 'histogram';
-
-export { VisImpl };
diff --git a/src/legacy/server/config/schema.js b/src/legacy/server/config/schema.js
index a24ffcbaaa49f..769d9ba311281 100644
--- a/src/legacy/server/config/schema.js
+++ b/src/legacy/server/config/schema.js
@@ -133,8 +133,8 @@ export default () =>
.keys({
enabled: Joi.boolean().default(false),
everyBytes: Joi.number()
- // > 100KB
- .greater(102399)
+ // > 1MB
+ .greater(1048576)
// < 1GB
.less(1073741825)
// 10MB
diff --git a/src/legacy/server/i18n/constants.ts b/src/legacy/server/i18n/constants.ts
new file mode 100644
index 0000000000000..96fa420d4c6e1
--- /dev/null
+++ b/src/legacy/server/i18n/constants.ts
@@ -0,0 +1,25 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export const I18N_RC = '.i18nrc.json';
+
+/**
+ * The type name used within the Monitoring index to publish localization stats.
+ */
+export const KIBANA_LOCALIZATION_STATS_TYPE = 'localization';
diff --git a/src/legacy/server/i18n/get_translations_path.js b/src/legacy/server/i18n/get_translations_path.ts
similarity index 85%
rename from src/legacy/server/i18n/get_translations_path.js
rename to src/legacy/server/i18n/get_translations_path.ts
index 6ac3e75e1d4a8..ac7c61dcf8543 100644
--- a/src/legacy/server/i18n/get_translations_path.js
+++ b/src/legacy/server/i18n/get_translations_path.ts
@@ -24,16 +24,20 @@ import globby from 'globby';
const readFileAsync = promisify(readFile);
-export async function getTranslationPaths({ cwd, glob }) {
+interface I18NRCFileStructure {
+ translations?: string[];
+}
+
+export async function getTranslationPaths({ cwd, glob }: { cwd: string; glob: string }) {
const entries = await globby(glob, { cwd });
- const translationPaths = [];
+ const translationPaths: string[] = [];
for (const entry of entries) {
const entryFullPath = resolve(cwd, entry);
const pluginBasePath = dirname(entryFullPath);
try {
const content = await readFileAsync(entryFullPath, 'utf8');
- const { translations } = JSON.parse(content);
+ const { translations } = JSON.parse(content) as I18NRCFileStructure;
if (translations && translations.length) {
translations.forEach(translation => {
const translationFullPath = resolve(pluginBasePath, translation);
diff --git a/src/legacy/server/i18n/index.js b/src/legacy/server/i18n/index.ts
similarity index 62%
rename from src/legacy/server/i18n/index.js
rename to src/legacy/server/i18n/index.ts
index e7fa5d5f6a5c0..9902aaa1e8914 100644
--- a/src/legacy/server/i18n/index.js
+++ b/src/legacy/server/i18n/index.ts
@@ -19,30 +19,35 @@
import { i18n, i18nLoader } from '@kbn/i18n';
import { basename } from 'path';
+import { Server } from 'hapi';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { fromRoot } from '../../../core/server/utils';
import { getTranslationPaths } from './get_translations_path';
import { I18N_RC } from './constants';
+import KbnServer, { KibanaConfig } from '../kbn_server';
+import { registerLocalizationUsageCollector } from './localization';
-export async function i18nMixin(kbnServer, server, config) {
- const locale = config.get('i18n.locale');
+export async function i18nMixin(kbnServer: KbnServer, server: Server, config: KibanaConfig) {
+ const locale = config.get('i18n.locale') as string;
const translationPaths = await Promise.all([
getTranslationPaths({
cwd: fromRoot('.'),
glob: I18N_RC,
}),
- ...config.get('plugins.paths').map(cwd => getTranslationPaths({ cwd, glob: I18N_RC })),
- ...config
- .get('plugins.scanDirs')
- .map(cwd => getTranslationPaths({ cwd, glob: `*/${I18N_RC}` })),
+ ...(config.get('plugins.paths') as string[]).map(cwd =>
+ getTranslationPaths({ cwd, glob: I18N_RC })
+ ),
+ ...(config.get('plugins.scanDirs') as string[]).map(cwd =>
+ getTranslationPaths({ cwd, glob: `*/${I18N_RC}` })
+ ),
getTranslationPaths({
cwd: fromRoot('../kibana-extra'),
glob: `*/${I18N_RC}`,
}),
]);
- const currentTranslationPaths = []
+ const currentTranslationPaths = ([] as string[])
.concat(...translationPaths)
.filter(translationPath => basename(translationPath, '.json') === locale);
i18nLoader.registerTranslationFiles(currentTranslationPaths);
@@ -55,5 +60,14 @@ export async function i18nMixin(kbnServer, server, config) {
})
);
- server.decorate('server', 'getTranslationsFilePaths', () => currentTranslationPaths);
+ const getTranslationsFilePaths = () => currentTranslationPaths;
+
+ server.decorate('server', 'getTranslationsFilePaths', getTranslationsFilePaths);
+
+ if (kbnServer.newPlatform.setup.plugins.usageCollection) {
+ registerLocalizationUsageCollector(kbnServer.newPlatform.setup.plugins.usageCollection, {
+ getLocale: () => config.get('i18n.locale') as string,
+ getTranslationsFilePaths,
+ });
+ }
}
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/localization/file_integrity.test.mocks.ts b/src/legacy/server/i18n/localization/file_integrity.test.mocks.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/localization/file_integrity.test.mocks.ts
rename to src/legacy/server/i18n/localization/file_integrity.test.mocks.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/localization/file_integrity.test.ts b/src/legacy/server/i18n/localization/file_integrity.test.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/localization/file_integrity.test.ts
rename to src/legacy/server/i18n/localization/file_integrity.test.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/localization/file_integrity.ts b/src/legacy/server/i18n/localization/file_integrity.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/localization/file_integrity.ts
rename to src/legacy/server/i18n/localization/file_integrity.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/localization/index.ts b/src/legacy/server/i18n/localization/index.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/localization/index.ts
rename to src/legacy/server/i18n/localization/index.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/localization/telemetry_localization_collector.test.ts b/src/legacy/server/i18n/localization/telemetry_localization_collector.test.ts
similarity index 94%
rename from src/legacy/core_plugins/telemetry/server/collectors/localization/telemetry_localization_collector.test.ts
rename to src/legacy/server/i18n/localization/telemetry_localization_collector.test.ts
index eec5cc8a065e4..cbe23da87c767 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/localization/telemetry_localization_collector.test.ts
+++ b/src/legacy/server/i18n/localization/telemetry_localization_collector.test.ts
@@ -22,16 +22,17 @@ interface TranslationsMock {
}
const createI18nLoaderMock = (translations: TranslationsMock) => {
- return {
+ return ({
getTranslationsByLocale() {
return {
messages: translations,
};
},
- };
+ } as unknown) as typeof i18nLoader;
};
import { getTranslationCount } from './telemetry_localization_collector';
+import { i18nLoader } from '@kbn/i18n';
describe('getTranslationCount', () => {
it('returns 0 if no translations registered', async () => {
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/localization/telemetry_localization_collector.ts b/src/legacy/server/i18n/localization/telemetry_localization_collector.ts
similarity index 71%
rename from src/legacy/core_plugins/telemetry/server/collectors/localization/telemetry_localization_collector.ts
rename to src/legacy/server/i18n/localization/telemetry_localization_collector.ts
index 191565187be14..89566dfd4ef68 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/localization/telemetry_localization_collector.ts
+++ b/src/legacy/server/i18n/localization/telemetry_localization_collector.ts
@@ -19,25 +19,36 @@
import { i18nLoader } from '@kbn/i18n';
import { size } from 'lodash';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { getIntegrityHashes, Integrities } from './file_integrity';
-import { KIBANA_LOCALIZATION_STATS_TYPE } from '../../../common/constants';
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
+import { KIBANA_LOCALIZATION_STATS_TYPE } from '../constants';
+
export interface UsageStats {
locale: string;
integrities: Integrities;
labelsCount?: number;
}
-export async function getTranslationCount(loader: any, locale: string): Promise {
+export interface LocalizationUsageCollectorHelpers {
+ getLocale: () => string;
+ getTranslationsFilePaths: () => string[];
+}
+
+export async function getTranslationCount(
+ loader: typeof i18nLoader,
+ locale: string
+): Promise {
const translations = await loader.getTranslationsByLocale(locale);
return size(translations.messages);
}
-export function createCollectorFetch(server: any) {
+export function createCollectorFetch({
+ getLocale,
+ getTranslationsFilePaths,
+}: LocalizationUsageCollectorHelpers) {
return async function fetchUsageStats(): Promise {
- const config = server.config();
- const locale: string = config.get('i18n.locale');
- const translationFilePaths: string[] = server.getTranslationsFilePaths();
+ const locale = getLocale();
+ const translationFilePaths: string[] = getTranslationsFilePaths();
const [labelsCount, integrities] = await Promise.all([
getTranslationCount(i18nLoader, locale),
@@ -54,12 +65,12 @@ export function createCollectorFetch(server: any) {
export function registerLocalizationUsageCollector(
usageCollection: UsageCollectionSetup,
- server: any
+ helpers: LocalizationUsageCollectorHelpers
) {
const collector = usageCollection.makeUsageCollector({
type: KIBANA_LOCALIZATION_STATS_TYPE,
isReady: () => true,
- fetch: createCollectorFetch(server),
+ fetch: createCollectorFetch(helpers),
});
usageCollection.registerCollector(collector);
diff --git a/src/legacy/server/kbn_server.d.ts b/src/legacy/server/kbn_server.d.ts
index 9952b345fa06f..d222dbb550f11 100644
--- a/src/legacy/server/kbn_server.d.ts
+++ b/src/legacy/server/kbn_server.d.ts
@@ -20,6 +20,7 @@
import { ResponseObject, Server } from 'hapi';
import { UnwrapPromise } from '@kbn/utility-types';
+import { TelemetryCollectionManagerPluginSetup } from 'src/plugins/telemetry_collection_manager/server';
import {
ConfigService,
CoreSetup,
@@ -104,6 +105,7 @@ type KbnMixinFunc = (kbnServer: KbnServer, server: Server, config: any) => Promi
export interface PluginsSetup {
usageCollection: UsageCollectionSetup;
+ telemetryCollectionManager: TelemetryCollectionManagerPluginSetup;
home: HomeServerPluginSetup;
[key: string]: object;
}
diff --git a/src/legacy/server/logging/rotate/log_rotator.test.ts b/src/legacy/server/logging/rotate/log_rotator.test.ts
index c2100546364d4..70842d42f5e1f 100644
--- a/src/legacy/server/logging/rotate/log_rotator.test.ts
+++ b/src/legacy/server/logging/rotate/log_rotator.test.ts
@@ -204,8 +204,8 @@ describe('LogRotator', () => {
expect(logRotator.running).toBe(true);
expect(logRotator.usePolling).toBe(false);
- const usePolling = await logRotator._shouldUsePolling();
- expect(usePolling).toBe(false);
+ const shouldUsePolling = await logRotator._shouldUsePolling();
+ expect(shouldUsePolling).toBe(false);
await logRotator.stop();
});
@@ -231,7 +231,8 @@ describe('LogRotator', () => {
await logRotator.start();
expect(logRotator.running).toBe(true);
- expect(logRotator.usePolling).toBe(true);
+ expect(logRotator.usePolling).toBe(false);
+ expect(logRotator.shouldUsePolling).toBe(true);
await logRotator.stop();
});
@@ -257,7 +258,8 @@ describe('LogRotator', () => {
await logRotator.start();
expect(logRotator.running).toBe(true);
- expect(logRotator.usePolling).toBe(true);
+ expect(logRotator.usePolling).toBe(false);
+ expect(logRotator.shouldUsePolling).toBe(true);
await logRotator.stop();
jest.useRealTimers();
diff --git a/src/legacy/server/logging/rotate/log_rotator.ts b/src/legacy/server/logging/rotate/log_rotator.ts
index 3662910ca5a7b..eeb91fd0f2636 100644
--- a/src/legacy/server/logging/rotate/log_rotator.ts
+++ b/src/legacy/server/logging/rotate/log_rotator.ts
@@ -50,6 +50,7 @@ export class LogRotator {
public usePolling: boolean;
public pollingInterval: number;
private stalkerUsePollingPolicyTestTimeout: NodeJS.Timeout | null;
+ public shouldUsePolling: boolean;
constructor(config: KibanaConfig, server: Server) {
this.config = config;
@@ -64,6 +65,7 @@ export class LogRotator {
this.stalker = null;
this.usePolling = config.get('logging.rotate.usePolling');
this.pollingInterval = config.get('logging.rotate.pollingInterval');
+ this.shouldUsePolling = false;
this.stalkerUsePollingPolicyTestTimeout = null;
}
@@ -150,12 +152,20 @@ export class LogRotator {
}
async _startLogFileSizeMonitor() {
- this.usePolling = await this._shouldUsePolling();
+ this.usePolling = this.config.get('logging.rotate.usePolling');
+ this.shouldUsePolling = await this._shouldUsePolling();
- if (this.usePolling && this.usePolling !== this.config.get('logging.rotate.usePolling')) {
+ if (this.usePolling && !this.shouldUsePolling) {
this.log(
['warning', 'logging:rotate'],
- 'The current environment does not support `fs.watch`. Falling back to polling using `fs.watchFile`'
+ 'Looks like your current environment support a faster algorithm then polling. You can try to disable `usePolling`'
+ );
+ }
+
+ if (!this.usePolling && this.shouldUsePolling) {
+ this.log(
+ ['error', 'logging:rotate'],
+ 'Looks like within your current environment you need to use polling in order to enable log rotator. Please enable `usePolling`'
);
}
diff --git a/src/legacy/ui/public/field_editor/__snapshots__/field_editor.test.js.snap b/src/legacy/ui/public/field_editor/__snapshots__/field_editor.test.js.snap
index 6c454370f59f5..19d12f4bbbd4c 100644
--- a/src/legacy/ui/public/field_editor/__snapshots__/field_editor.test.js.snap
+++ b/src/legacy/ui/public/field_editor/__snapshots__/field_editor.test.js.snap
@@ -945,6 +945,10 @@ exports[`FieldEditor should show deprecated lang warning 1`] = `
"text": "_source",
"value": "_source",
},
+ Object {
+ "text": "histogram",
+ "value": "histogram",
+ },
Object {
"text": "conflict",
"value": "conflict",
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 c58a7d2fbb5cd..809022620e69d 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
@@ -271,6 +271,12 @@ export const npSetup = {
}),
},
},
+ discover: {
+ docViews: {
+ addDocView: sinon.fake(),
+ setAngularInjectorGetter: sinon.fake(),
+ },
+ },
visTypeVega: {
config: sinon.fake(),
},
@@ -459,6 +465,11 @@ export const npStart = {
useChartsTheme: sinon.fake(),
},
},
+ discover: {
+ docViews: {
+ DocViewer: () => null,
+ },
+ },
},
};
diff --git a/src/legacy/ui/public/new_platform/new_platform.ts b/src/legacy/ui/public/new_platform/new_platform.ts
index deb8387fee29c..ee14f192a2149 100644
--- a/src/legacy/ui/public/new_platform/new_platform.ts
+++ b/src/legacy/ui/public/new_platform/new_platform.ts
@@ -65,6 +65,7 @@ import {
NavigationPublicPluginStart,
} from '../../../../plugins/navigation/public';
import { VisTypeVegaSetup } from '../../../../plugins/vis_type_vega/public';
+import { DiscoverSetup, DiscoverStart } from '../../../../plugins/discover/public';
export interface PluginsSetup {
bfetch: BfetchPublicSetup;
@@ -83,6 +84,7 @@ export interface PluginsSetup {
advancedSettings: AdvancedSettingsSetup;
management: ManagementSetup;
visTypeVega: VisTypeVegaSetup;
+ discover: DiscoverSetup;
telemetry?: TelemetryPluginSetup;
}
@@ -100,6 +102,7 @@ export interface PluginsStart {
share: SharePluginStart;
management: ManagementStart;
advancedSettings: AdvancedSettingsStart;
+ discover: DiscoverStart;
telemetry?: TelemetryPluginStart;
}
diff --git a/src/plugins/dashboard/public/actions/replace_panel_action.tsx b/src/plugins/dashboard/public/actions/replace_panel_action.tsx
index 21ec961917d17..4e20aa3c35088 100644
--- a/src/plugins/dashboard/public/actions/replace_panel_action.tsx
+++ b/src/plugins/dashboard/public/actions/replace_panel_action.tsx
@@ -37,7 +37,7 @@ export interface ReplacePanelActionContext {
export class ReplacePanelAction implements ActionByType {
public readonly type = ACTION_REPLACE_PANEL;
public readonly id = ACTION_REPLACE_PANEL;
- public order = 11;
+ public order = 3;
constructor(
private core: CoreStart,
diff --git a/src/plugins/dashboard/public/plugin.tsx b/src/plugins/dashboard/public/plugin.tsx
index 8a6e747aac170..d663c736e5aed 100644
--- a/src/plugins/dashboard/public/plugin.tsx
+++ b/src/plugins/dashboard/public/plugin.tsx
@@ -78,7 +78,7 @@ export class DashboardEmbeddableContainerPublicPlugin
): Setup {
const expandPanelAction = new ExpandPanelAction();
uiActions.registerAction(expandPanelAction);
- uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction);
+ uiActions.attachAction(CONTEXT_MENU_TRIGGER, expandPanelAction.id);
const startServices = core.getStartServices();
if (share) {
@@ -134,7 +134,7 @@ export class DashboardEmbeddableContainerPublicPlugin
plugins.embeddable.getEmbeddableFactories
);
uiActions.registerAction(changeViewAction);
- uiActions.attachAction(CONTEXT_MENU_TRIGGER, changeViewAction);
+ uiActions.addTriggerAction(CONTEXT_MENU_TRIGGER, changeViewAction);
}
public stop() {}
diff --git a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx
index a81d80b440e04..4aede3f3442fb 100644
--- a/src/plugins/dashboard/public/tests/dashboard_container.test.tsx
+++ b/src/plugins/dashboard/public/tests/dashboard_container.test.tsx
@@ -49,7 +49,7 @@ test('DashboardContainer in edit mode shows edit mode actions', async () => {
const editModeAction = createEditModeAction();
uiActionsSetup.registerAction(editModeAction);
- uiActionsSetup.attachAction(CONTEXT_MENU_TRIGGER, editModeAction);
+ uiActionsSetup.addTriggerAction(CONTEXT_MENU_TRIGGER, editModeAction);
setup.registerEmbeddableFactory(
CONTACT_CARD_EMBEDDABLE,
new ContactCardEmbeddableFactory({} as any, (() => null) as any, {} as any)
diff --git a/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts b/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts
index 09fc4555992a8..a3fe19fa9b2fc 100644
--- a/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts
+++ b/src/plugins/data/common/kbn_field_types/kbn_field_types.test.ts
@@ -87,6 +87,7 @@ describe('utils/kbn_field_types', () => {
KBN_FIELD_TYPES.DATE,
KBN_FIELD_TYPES.GEO_POINT,
KBN_FIELD_TYPES.GEO_SHAPE,
+ KBN_FIELD_TYPES.HISTOGRAM,
KBN_FIELD_TYPES.IP,
KBN_FIELD_TYPES.MURMUR3,
KBN_FIELD_TYPES.NESTED,
diff --git a/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts b/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts
index 192e8bc4f3727..cb9357eb9865e 100644
--- a/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts
+++ b/src/plugins/data/common/kbn_field_types/kbn_field_types_factory.ts
@@ -95,6 +95,11 @@ export const createKbnFieldTypes = (): KbnFieldType[] => [
name: KBN_FIELD_TYPES._SOURCE,
esTypes: [ES_FIELD_TYPES._SOURCE],
}),
+ new KbnFieldType({
+ name: KBN_FIELD_TYPES.HISTOGRAM,
+ filterable: true,
+ esTypes: [ES_FIELD_TYPES.HISTOGRAM],
+ }),
new KbnFieldType({
name: KBN_FIELD_TYPES.CONFLICT,
}),
diff --git a/src/plugins/data/common/kbn_field_types/types.ts b/src/plugins/data/common/kbn_field_types/types.ts
index 11c62e8f86dce..acd7a36b01fb3 100644
--- a/src/plugins/data/common/kbn_field_types/types.ts
+++ b/src/plugins/data/common/kbn_field_types/types.ts
@@ -59,6 +59,8 @@ export enum ES_FIELD_TYPES {
ATTACHMENT = 'attachment',
TOKEN_COUNT = 'token_count',
MURMUR3 = 'murmur3',
+
+ HISTOGRAM = 'histogram',
}
/** @public **/
@@ -77,4 +79,5 @@ export enum KBN_FIELD_TYPES {
CONFLICT = 'conflict',
OBJECT = 'object',
NESTED = 'nested',
+ HISTOGRAM = 'histogram',
}
diff --git a/src/plugins/data/public/field_formats/utils/deserialize.ts b/src/plugins/data/public/field_formats/utils/deserialize.ts
index c735ad196fbee..840e023a11589 100644
--- a/src/plugins/data/public/field_formats/utils/deserialize.ts
+++ b/src/plugins/data/public/field_formats/utils/deserialize.ts
@@ -70,7 +70,8 @@ export const deserializeFieldFormat: FormatFactory = function(
const { id } = mapping;
if (id === 'range') {
const RangeFormat = FieldFormat.from((range: any) => {
- const format = getFieldFormat(this, id, mapping.params);
+ const nestedFormatter = mapping.params as SerializedFieldFormat;
+ const format = getFieldFormat(this, nestedFormatter.id, nestedFormatter.params);
const gte = '\u2265';
const lt = '\u003c';
return i18n.translate('data.aggTypes.buckets.ranges.rangesFormatMessage', {
diff --git a/src/plugins/data/public/index.ts b/src/plugins/data/public/index.ts
index 339a5fea91c5f..977b9568ceaa6 100644
--- a/src/plugins/data/public/index.ts
+++ b/src/plugins/data/public/index.ts
@@ -328,6 +328,7 @@ export {
AggParamType,
AggTypeFieldFilters, // TODO convert to interface
AggTypeFilters, // TODO convert to interface
+ AggConfigOptions,
BUCKET_TYPES,
DateRangeKey, // only used in field formatter deserialization, which will live in data
IAggConfig,
diff --git a/src/plugins/data/public/plugin.ts b/src/plugins/data/public/plugin.ts
index fc5dde94fa851..ea2e85947aa12 100644
--- a/src/plugins/data/public/plugin.ts
+++ b/src/plugins/data/public/plugin.ts
@@ -109,12 +109,12 @@ export class DataPublicPlugin implements Plugin;
+ // (undocumented)
+ schema?: string;
+ // (undocumented)
+ type: IAggType;
+}
+
// Warning: (ae-missing-release-tag) "AggGroupNames" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -284,6 +300,8 @@ export enum ES_FIELD_TYPES {
// (undocumented)
HALF_FLOAT = "half_float",
// (undocumented)
+ HISTOGRAM = "histogram",
+ // (undocumented)
_ID = "_id",
// (undocumented)
_INDEX = "_index",
@@ -1126,6 +1144,8 @@ export enum KBN_FIELD_TYPES {
// (undocumented)
GEO_SHAPE = "geo_shape",
// (undocumented)
+ HISTOGRAM = "histogram",
+ // (undocumented)
IP = "ip",
// (undocumented)
MURMUR3 = "murmur3",
@@ -1828,21 +1848,21 @@ export type TSearchStrategyProvider = (context: ISearc
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "flattenHitWrapper" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "getRoutes" needs to be exported by the entry point index.d.ts
// src/plugins/data/public/index.ts:234:27 - (ae-forgotten-export) The symbol "formatHitProvider" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:379:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:379:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:379:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:379:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:384:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:387:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:396:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:401:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:405:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
-// src/plugins/data/public/index.ts:409:1 - (ae-forgotten-export) The symbol "toAbsoluteDates" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getRequestInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "getResponseInspectorStats" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyAggResponse" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:380:20 - (ae-forgotten-export) The symbol "tabifyGetColumns" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:385:1 - (ae-forgotten-export) The symbol "CidrMask" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:386:1 - (ae-forgotten-export) The symbol "convertDateRangeToString" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:388:1 - (ae-forgotten-export) The symbol "dateHistogramInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:397:1 - (ae-forgotten-export) The symbol "InvalidEsCalendarIntervalError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:398:1 - (ae-forgotten-export) The symbol "InvalidEsIntervalFormatError" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:399:1 - (ae-forgotten-export) The symbol "isDateHistogramBucketAggConfig" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:402:1 - (ae-forgotten-export) The symbol "isValidEsInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:403:1 - (ae-forgotten-export) The symbol "isValidInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:406:1 - (ae-forgotten-export) The symbol "parseInterval" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:407:1 - (ae-forgotten-export) The symbol "propFilter" needs to be exported by the entry point index.d.ts
+// src/plugins/data/public/index.ts:410: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/metrics/cardinality.ts b/src/plugins/data/public/search/aggs/metrics/cardinality.ts
index aa41307b2a052..88cdf3175665e 100644
--- a/src/plugins/data/public/search/aggs/metrics/cardinality.ts
+++ b/src/plugins/data/public/search/aggs/metrics/cardinality.ts
@@ -45,6 +45,9 @@ export const cardinalityMetricAgg = new MetricAggType({
{
name: 'field',
type: 'field',
+ filterFieldTypes: Object.values(KBN_FIELD_TYPES).filter(
+ type => type !== KBN_FIELD_TYPES.HISTOGRAM
+ ),
},
],
});
diff --git a/src/plugins/data/public/search/aggs/metrics/median.ts b/src/plugins/data/public/search/aggs/metrics/median.ts
index f2636d52e3484..faa0694cd5312 100644
--- a/src/plugins/data/public/search/aggs/metrics/median.ts
+++ b/src/plugins/data/public/search/aggs/metrics/median.ts
@@ -40,7 +40,7 @@ export const medianMetricAgg = new MetricAggType({
{
name: 'field',
type: 'field',
- filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE],
+ filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE, KBN_FIELD_TYPES.HISTOGRAM],
write(agg, output) {
output.params.field = agg.getParam('field').name;
output.params.percents = [50];
diff --git a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts
index 71b1c1415d98e..7dc0f70ea7b80 100644
--- a/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts
+++ b/src/plugins/data/public/search/aggs/metrics/percentile_ranks.ts
@@ -59,7 +59,7 @@ export const percentileRanksMetricAgg = new MetricAggType({
{
name: 'field',
type: 'field',
- filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE],
+ filterFieldTypes: [KBN_FIELD_TYPES.NUMBER, KBN_FIELD_TYPES.DATE, KBN_FIELD_TYPES.HISTOGRAM],
},
{
name: 'percents',
diff --git a/src/plugins/data/public/search/aggs/metrics/top_hit.ts b/src/plugins/data/public/search/aggs/metrics/top_hit.ts
index 738de6b62bccb..d0c668c577e62 100644
--- a/src/plugins/data/public/search/aggs/metrics/top_hit.ts
+++ b/src/plugins/data/public/search/aggs/metrics/top_hit.ts
@@ -60,7 +60,9 @@ export const topHitMetricAgg = new MetricAggType({
name: 'field',
type: 'field',
onlyAggregatable: false,
- filterFieldTypes: '*',
+ filterFieldTypes: Object.values(KBN_FIELD_TYPES).filter(
+ type => type !== KBN_FIELD_TYPES.HISTOGRAM
+ ),
write(agg, output) {
const field = agg.getParam('field');
output.params = {};
diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md
index 178b2949a9456..1abc74fe07ccc 100644
--- a/src/plugins/data/server/server.api.md
+++ b/src/plugins/data/server/server.api.md
@@ -7,7 +7,6 @@
import { APICaller as APICaller_2 } from 'kibana/server';
import Boom from 'boom';
import { BulkIndexDocumentsParams } from 'elasticsearch';
-import { CallCluster as CallCluster_2 } from 'src/legacy/core_plugins/elasticsearch';
import { CatAliasesParams } from 'elasticsearch';
import { CatAllocationParams } from 'elasticsearch';
import { CatCommonParams } from 'elasticsearch';
@@ -176,6 +175,8 @@ export enum ES_FIELD_TYPES {
// (undocumented)
HALF_FLOAT = "half_float",
// (undocumented)
+ HISTOGRAM = "histogram",
+ // (undocumented)
_ID = "_id",
// (undocumented)
_INDEX = "_index",
@@ -547,6 +548,8 @@ export enum KBN_FIELD_TYPES {
// (undocumented)
GEO_SHAPE = "geo_shape",
// (undocumented)
+ HISTOGRAM = "histogram",
+ // (undocumented)
IP = "ip",
// (undocumented)
MURMUR3 = "murmur3",
diff --git a/src/plugins/dev_tools/public/plugin.ts b/src/plugins/dev_tools/public/plugin.ts
index 9ebfeb5387b26..df61271baf879 100644
--- a/src/plugins/dev_tools/public/plugin.ts
+++ b/src/plugins/dev_tools/public/plugin.ts
@@ -132,4 +132,6 @@ export class DevToolsPlugin implements Plugin {
getSortedDevTools: this.getSortedDevTools.bind(this),
};
}
+
+ public stop() {}
}
diff --git a/src/plugins/discover/kibana.json b/src/plugins/discover/kibana.json
new file mode 100644
index 0000000000000..91d6358d44c18
--- /dev/null
+++ b/src/plugins/discover/kibana.json
@@ -0,0 +1,6 @@
+{
+ "id": "discover",
+ "version": "kibana",
+ "server": false,
+ "ui": true
+}
diff --git a/src/plugins/discover/public/components/_index.scss b/src/plugins/discover/public/components/_index.scss
new file mode 100644
index 0000000000000..ff50d4b5dca93
--- /dev/null
+++ b/src/plugins/discover/public/components/_index.scss
@@ -0,0 +1 @@
+@import 'doc_viewer/index';
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap b/src/plugins/discover/public/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap
rename to src/plugins/discover/public/components/doc_viewer/__snapshots__/doc_viewer.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap b/src/plugins/discover/public/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap
rename to src/plugins/discover/public/components/doc_viewer/__snapshots__/doc_viewer_render_tab.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/_doc_viewer.scss b/src/plugins/discover/public/components/doc_viewer/_doc_viewer.scss
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/_doc_viewer.scss
rename to src/plugins/discover/public/components/doc_viewer/_doc_viewer.scss
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/_index.scss b/src/plugins/discover/public/components/doc_viewer/_index.scss
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/_index.scss
rename to src/plugins/discover/public/components/doc_viewer/_index.scss
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer.test.tsx b/src/plugins/discover/public/components/doc_viewer/doc_viewer.test.tsx
similarity index 81%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer.test.tsx
rename to src/plugins/discover/public/components/doc_viewer/doc_viewer.test.tsx
index 15f0f40700abc..6f29f10ddd026 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer.test.tsx
+++ b/src/plugins/discover/public/components/doc_viewer/doc_viewer.test.tsx
@@ -21,37 +21,33 @@ import { mount, shallow } from 'enzyme';
import { DocViewer } from './doc_viewer';
// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
-import { getServices } from '../../../kibana_services';
+import { getDocViewsRegistry } from '../../services';
import { DocViewRenderProps } from '../../doc_views/doc_views_types';
-jest.mock('../../../kibana_services', () => {
+jest.mock('../../services', () => {
let registry: any[] = [];
return {
- getServices: () => ({
- docViewsRegistry: {
- addDocView(view: any) {
- registry.push(view);
- },
- getDocViewsSorted() {
- return registry;
- },
+ getDocViewsRegistry: () => ({
+ addDocView(view: any) {
+ registry.push(view);
+ },
+ getDocViewsSorted() {
+ return registry;
},
resetRegistry: () => {
registry = [];
},
}),
- formatMsg: (x: any) => String(x),
- formatStack: (x: any) => String(x),
};
});
beforeEach(() => {
- (getServices() as any).resetRegistry();
+ (getDocViewsRegistry() as any).resetRegistry();
jest.clearAllMocks();
});
test('Render with 3 different tabs', () => {
- const registry = getServices().docViewsRegistry;
+ const registry = getDocViewsRegistry();
registry.addDocView({ order: 10, title: 'Render function', render: jest.fn() });
registry.addDocView({ order: 20, title: 'React component', component: () => test
});
registry.addDocView({ order: 30, title: 'Invalid doc view' });
@@ -69,7 +65,7 @@ test('Render with 1 tab displaying error message', () => {
return null;
}
- const registry = getServices().docViewsRegistry;
+ const registry = getDocViewsRegistry();
registry.addDocView({
order: 10,
title: 'React component',
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer.tsx b/src/plugins/discover/public/components/doc_viewer/doc_viewer.tsx
similarity index 95%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer.tsx
rename to src/plugins/discover/public/components/doc_viewer/doc_viewer.tsx
index a177d8c29304c..792d9c44400d7 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer.tsx
+++ b/src/plugins/discover/public/components/doc_viewer/doc_viewer.tsx
@@ -18,7 +18,7 @@
*/
import React from 'react';
import { EuiTabbedContent } from '@elastic/eui';
-import { getServices } from '../../../kibana_services';
+import { getDocViewsRegistry } from '../../services';
import { DocViewerTab } from './doc_viewer_tab';
import { DocView, DocViewRenderProps } from '../../doc_views/doc_views_types';
@@ -29,7 +29,7 @@ import { DocView, DocViewRenderProps } from '../../doc_views/doc_views_types';
* a `render` function.
*/
export function DocViewer(renderProps: DocViewRenderProps) {
- const { docViewsRegistry } = getServices();
+ const docViewsRegistry = getDocViewsRegistry();
const tabs = docViewsRegistry
.getDocViewsSorted(renderProps.hit)
.map(({ title, render, component }: DocView, idx: number) => {
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_render_error.tsx b/src/plugins/discover/public/components/doc_viewer/doc_viewer_render_error.tsx
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_render_error.tsx
rename to src/plugins/discover/public/components/doc_viewer/doc_viewer_render_error.tsx
index 075217add7b52..387e57dc8a7e3 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_render_error.tsx
+++ b/src/plugins/discover/public/components/doc_viewer/doc_viewer_render_error.tsx
@@ -18,7 +18,7 @@
*/
import React from 'react';
import { EuiCallOut, EuiCodeBlock } from '@elastic/eui';
-import { formatMsg, formatStack } from '../../../kibana_services';
+import { formatMsg, formatStack } from '../../../../kibana_legacy/public';
interface Props {
error: Error | string;
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_render_tab.test.tsx b/src/plugins/discover/public/components/doc_viewer/doc_viewer_render_tab.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_render_tab.test.tsx
rename to src/plugins/discover/public/components/doc_viewer/doc_viewer_render_tab.test.tsx
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_render_tab.tsx b/src/plugins/discover/public/components/doc_viewer/doc_viewer_render_tab.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_render_tab.tsx
rename to src/plugins/discover/public/components/doc_viewer/doc_viewer_render_tab.tsx
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_tab.tsx b/src/plugins/discover/public/components/doc_viewer/doc_viewer_tab.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/doc_viewer/doc_viewer_tab.tsx
rename to src/plugins/discover/public/components/doc_viewer/doc_viewer_tab.tsx
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/__snapshots__/field_name.test.tsx.snap b/src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/__snapshots__/field_name.test.tsx.snap
rename to src/plugins/discover/public/components/field_name/__snapshots__/field_name.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.test.tsx b/src/plugins/discover/public/components/field_name/field_name.test.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.test.tsx
rename to src/plugins/discover/public/components/field_name/field_name.test.tsx
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx b/src/plugins/discover/public/components/field_name/field_name.tsx
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx
rename to src/plugins/discover/public/components/field_name/field_name.tsx
index 1b3b16332fa4f..63518aae28de6 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_name.tsx
+++ b/src/plugins/discover/public/components/field_name/field_name.tsx
@@ -20,8 +20,8 @@ import React from 'react';
import classNames from 'classnames';
import { EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
-import { FieldIcon, FieldIconProps } from '../../../../../../../../../plugins/kibana_react/public';
-import { shortenDottedString } from '../../../helpers';
+import { FieldIcon, FieldIconProps } from '../../../../kibana_react/public';
+import { shortenDottedString } from '../../helpers';
import { getFieldTypeName } from './field_type_name';
// property field is provided at discover's field chooser
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_type_name.ts b/src/plugins/discover/public/components/field_name/field_type_name.ts
similarity index 66%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_type_name.ts
rename to src/plugins/discover/public/components/field_name/field_type_name.ts
index 0cf428ee48b9d..a67c20fc4f353 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/angular/directives/field_name/field_type_name.ts
+++ b/src/plugins/discover/public/components/field_name/field_type_name.ts
@@ -21,52 +21,52 @@ import { i18n } from '@kbn/i18n';
export function getFieldTypeName(type: string) {
switch (type) {
case 'boolean':
- return i18n.translate('kbn.discover.fieldNameIcons.booleanAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.booleanAriaLabel', {
defaultMessage: 'Boolean field',
});
case 'conflict':
- return i18n.translate('kbn.discover.fieldNameIcons.conflictFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.conflictFieldAriaLabel', {
defaultMessage: 'Conflicting field',
});
case 'date':
- return i18n.translate('kbn.discover.fieldNameIcons.dateFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.dateFieldAriaLabel', {
defaultMessage: 'Date field',
});
case 'geo_point':
- return i18n.translate('kbn.discover.fieldNameIcons.geoPointFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.geoPointFieldAriaLabel', {
defaultMessage: 'Geo point field',
});
case 'geo_shape':
- return i18n.translate('kbn.discover.fieldNameIcons.geoShapeFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.geoShapeFieldAriaLabel', {
defaultMessage: 'Geo shape field',
});
case 'ip':
- return i18n.translate('kbn.discover.fieldNameIcons.ipAddressFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.ipAddressFieldAriaLabel', {
defaultMessage: 'IP address field',
});
case 'murmur3':
- return i18n.translate('kbn.discover.fieldNameIcons.murmur3FieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.murmur3FieldAriaLabel', {
defaultMessage: 'Murmur3 field',
});
case 'number':
- return i18n.translate('kbn.discover.fieldNameIcons.numberFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.numberFieldAriaLabel', {
defaultMessage: 'Number field',
});
case 'source':
// Note that this type is currently not provided, type for _source is undefined
- return i18n.translate('kbn.discover.fieldNameIcons.sourceFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.sourceFieldAriaLabel', {
defaultMessage: 'Source field',
});
case 'string':
- return i18n.translate('kbn.discover.fieldNameIcons.stringFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.stringFieldAriaLabel', {
defaultMessage: 'String field',
});
case 'nested':
- return i18n.translate('kbn.discover.fieldNameIcons.nestedFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.nestedFieldAriaLabel', {
defaultMessage: 'Nested field',
});
default:
- return i18n.translate('kbn.discover.fieldNameIcons.unknownFieldAriaLabel', {
+ return i18n.translate('discover.fieldNameIcons.unknownFieldAriaLabel', {
defaultMessage: 'Unknown field',
});
}
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap b/src/plugins/discover/public/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap
rename to src/plugins/discover/public/components/json_code_block/__snapshots__/json_code_block.test.tsx.snap
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/json_code_block.test.tsx b/src/plugins/discover/public/components/json_code_block/json_code_block.test.tsx
similarity index 95%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/json_code_block.test.tsx
rename to src/plugins/discover/public/components/json_code_block/json_code_block.test.tsx
index 9cab7974c9eb2..7e7f80c6aaa56 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/json_code_block.test.tsx
+++ b/src/plugins/discover/public/components/json_code_block/json_code_block.test.tsx
@@ -19,7 +19,7 @@
import React from 'react';
import { shallow } from 'enzyme';
import { JsonCodeBlock } from './json_code_block';
-import { IndexPattern } from '../../../kibana_services';
+import { IndexPattern } from '../../../../data/public';
it('returns the `JsonCodeEditor` component', () => {
const props = {
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/json_code_block.tsx b/src/plugins/discover/public/components/json_code_block/json_code_block.tsx
similarity index 93%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/json_code_block.tsx
rename to src/plugins/discover/public/components/json_code_block/json_code_block.tsx
index 3331969e351ab..9297ab0dfcf4d 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/json_code_block/json_code_block.tsx
+++ b/src/plugins/discover/public/components/json_code_block/json_code_block.tsx
@@ -22,7 +22,7 @@ import { i18n } from '@kbn/i18n';
import { DocViewRenderProps } from '../../doc_views/doc_views_types';
export function JsonCodeBlock({ hit }: DocViewRenderProps) {
- const label = i18n.translate('kbn.discover.docViews.json.codeEditorAriaLabel', {
+ const label = i18n.translate('discover.docViews.json.codeEditorAriaLabel', {
defaultMessage: 'Read only JSON view of an elasticsearch document',
});
return (
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table.test.tsx b/src/plugins/discover/public/components/table/table.test.tsx
similarity index 98%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table.test.tsx
rename to src/plugins/discover/public/components/table/table.test.tsx
index 386f405544a61..91e116c4c6696 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table.test.tsx
+++ b/src/plugins/discover/public/components/table/table.test.tsx
@@ -21,10 +21,7 @@ import { mount } from 'enzyme';
// @ts-ignore
import { findTestSubject } from '@elastic/eui/lib/test';
import { DocViewTable } from './table';
-
-import { IndexPattern, indexPatterns } from '../../../kibana_services';
-
-jest.mock('ui/new_platform');
+import { indexPatterns, IndexPattern } from '../../../../data/public';
const indexPattern = {
fields: [
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table.tsx b/src/plugins/discover/public/components/table/table.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table.tsx
rename to src/plugins/discover/public/components/table/table.tsx
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_helper.test.ts b/src/plugins/discover/public/components/table/table_helper.test.ts
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_helper.test.ts
rename to src/plugins/discover/public/components/table/table_helper.test.ts
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_helper.tsx b/src/plugins/discover/public/components/table/table_helper.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_helper.tsx
rename to src/plugins/discover/public/components/table/table_helper.tsx
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row.tsx b/src/plugins/discover/public/components/table/table_row.tsx
similarity index 98%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row.tsx
rename to src/plugins/discover/public/components/table/table_row.tsx
index 5b13f6b3655c3..a4d5c57d10b33 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row.tsx
+++ b/src/plugins/discover/public/components/table/table_row.tsx
@@ -26,7 +26,7 @@ import { DocViewTableRowBtnCollapse } from './table_row_btn_collapse';
import { DocViewTableRowBtnFilterExists } from './table_row_btn_filter_exists';
import { DocViewTableRowIconNoMapping } from './table_row_icon_no_mapping';
import { DocViewTableRowIconUnderscore } from './table_row_icon_underscore';
-import { FieldName } from '../../angular/directives/field_name/field_name';
+import { FieldName } from '../field_name/field_name';
export interface Props {
field: string;
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_btn_collapse.tsx b/src/plugins/discover/public/components/table/table_row_btn_collapse.tsx
similarity index 94%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_btn_collapse.tsx
rename to src/plugins/discover/public/components/table/table_row_btn_collapse.tsx
index e59f607329d4a..bb5ea4bd20f07 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_btn_collapse.tsx
+++ b/src/plugins/discover/public/components/table/table_row_btn_collapse.tsx
@@ -26,7 +26,7 @@ export interface Props {
}
export function DocViewTableRowBtnCollapse({ onClick, isCollapsed }: Props) {
- const label = i18n.translate('kbn.discover.docViews.table.toggleFieldDetails', {
+ const label = i18n.translate('discover.docViews.table.toggleFieldDetails', {
defaultMessage: 'Toggle field details',
});
return (
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_btn_filter_add.tsx b/src/plugins/discover/public/components/table/table_row_btn_filter_add.tsx
similarity index 87%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_btn_filter_add.tsx
rename to src/plugins/discover/public/components/table/table_row_btn_filter_add.tsx
index 8e2668e26cf08..bd842eb5c6f72 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_btn_filter_add.tsx
+++ b/src/plugins/discover/public/components/table/table_row_btn_filter_add.tsx
@@ -29,12 +29,12 @@ export interface Props {
export function DocViewTableRowBtnFilterAdd({ onClick, disabled = false }: Props) {
const tooltipContent = disabled ? (
) : (
);
@@ -42,7 +42,7 @@ export function DocViewTableRowBtnFilterAdd({ onClick, disabled = false }: Props
return (
) : (
)
) : (
);
@@ -54,12 +54,9 @@ export function DocViewTableRowBtnFilterExists({
return (
) : (
);
@@ -42,7 +42,7 @@ export function DocViewTableRowBtnFilterRemove({ onClick, disabled = false }: Pr
return (
}
>
Index Patterns page',
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_icon_underscore.tsx b/src/plugins/discover/public/components/table/table_row_icon_underscore.tsx
similarity index 89%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_icon_underscore.tsx
rename to src/plugins/discover/public/components/table/table_row_icon_underscore.tsx
index 724b5712cf1fe..791ab18de5175 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/components/table/table_row_icon_underscore.tsx
+++ b/src/plugins/discover/public/components/table/table_row_icon_underscore.tsx
@@ -22,13 +22,13 @@ import { i18n } from '@kbn/i18n';
export function DocViewTableRowIconUnderscore() {
const ariaLabel = i18n.translate(
- 'kbn.discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel',
+ 'discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedAriaLabel',
{
defaultMessage: 'Warning',
}
);
const tooltipContent = i18n.translate(
- 'kbn.discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip',
+ 'discover.docViews.table.fieldNamesBeginningWithUnderscoreUnsupportedTooltip',
{
defaultMessage: 'Field names beginning with {underscoreSign} are not supported',
values: { underscoreSign: '_' },
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_helpers.tsx b/src/plugins/discover/public/doc_views/doc_views_helpers.tsx
similarity index 100%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_helpers.tsx
rename to src/plugins/discover/public/doc_views/doc_views_helpers.tsx
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_registry.ts b/src/plugins/discover/public/doc_views/doc_views_registry.ts
similarity index 82%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_registry.ts
rename to src/plugins/discover/public/doc_views/doc_views_registry.ts
index 91acf1c7ac4ae..8f4518538be72 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_registry.ts
+++ b/src/plugins/discover/public/doc_views/doc_views_registry.ts
@@ -23,8 +23,11 @@ import { DocView, DocViewInput, ElasticSearchHit, DocViewInputFn } from './doc_v
export class DocViewsRegistry {
private docViews: DocView[] = [];
+ private angularInjectorGetter: (() => Promise) | null = null;
- constructor(private getInjector: () => Promise) {}
+ setAngularInjectorGetter(injectorGetter: () => Promise) {
+ this.angularInjectorGetter = injectorGetter;
+ }
/**
* Extends and adds the given doc view to the registry array
@@ -33,7 +36,12 @@ export class DocViewsRegistry {
const docView = typeof docViewRaw === 'function' ? docViewRaw() : docViewRaw;
if (docView.directive) {
// convert angular directive to render function for backwards compatibility
- docView.render = convertDirectiveToRenderFn(docView.directive, this.getInjector);
+ docView.render = convertDirectiveToRenderFn(docView.directive, () => {
+ if (!this.angularInjectorGetter) {
+ throw new Error('Angular was not initialized');
+ }
+ return this.angularInjectorGetter();
+ });
}
if (typeof docView.shouldShow !== 'function') {
docView.shouldShow = () => true;
diff --git a/src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_types.ts b/src/plugins/discover/public/doc_views/doc_views_types.ts
similarity index 90%
rename from src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_types.ts
rename to src/plugins/discover/public/doc_views/doc_views_types.ts
index a7828f9f0e7ed..0a4b5bb570bd7 100644
--- a/src/legacy/core_plugins/kibana/public/discover/np_ready/doc_views/doc_views_types.ts
+++ b/src/plugins/discover/public/doc_views/doc_views_types.ts
@@ -18,10 +18,10 @@
*/
import { ComponentType } from 'react';
import { IScope } from 'angular';
-import { IndexPattern } from '../../kibana_services';
+import { IndexPattern } from '../../../data/public';
export interface AngularDirective {
- controller: (scope: AngularScope) => void;
+ controller: (...injectedServices: any[]) => void;
template: string;
}
@@ -51,13 +51,14 @@ export interface DocViewRenderProps {
onAddColumn?: (columnName: string) => void;
onRemoveColumn?: (columnName: string) => void;
}
+export type DocViewerComponent = ComponentType;
export type DocViewRenderFn = (
domeNode: HTMLDivElement,
renderProps: DocViewRenderProps
) => () => void;
export interface DocViewInput {
- component?: ComponentType;
+ component?: DocViewerComponent;
directive?: AngularDirective;
order: number;
render?: DocViewRenderFn;
diff --git a/src/legacy/core_plugins/telemetry/public/views/management/index.ts b/src/plugins/discover/public/helpers/index.ts
similarity index 92%
rename from src/legacy/core_plugins/telemetry/public/views/management/index.ts
rename to src/plugins/discover/public/helpers/index.ts
index 2e9f064ec80d8..7196c96989e97 100644
--- a/src/legacy/core_plugins/telemetry/public/views/management/index.ts
+++ b/src/plugins/discover/public/helpers/index.ts
@@ -17,4 +17,4 @@
* under the License.
*/
-import './management';
+export { shortenDottedString } from './shorten_dotted_string';
diff --git a/src/plugins/discover/public/helpers/shorten_dotted_string.ts b/src/plugins/discover/public/helpers/shorten_dotted_string.ts
new file mode 100644
index 0000000000000..9d78a96784339
--- /dev/null
+++ b/src/plugins/discover/public/helpers/shorten_dotted_string.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.
+ */
+
+const DOT_PREFIX_RE = /(.).+?\./g;
+
+/**
+ * Convert a dot.notated.string into a short
+ * version (d.n.string)
+ */
+export const shortenDottedString = (input: string) => input.replace(DOT_PREFIX_RE, '$1.');
diff --git a/src/plugins/discover/public/index.scss b/src/plugins/discover/public/index.scss
new file mode 100644
index 0000000000000..841415620d691
--- /dev/null
+++ b/src/plugins/discover/public/index.scss
@@ -0,0 +1 @@
+@import 'components/index';
diff --git a/src/plugins/discover/public/index.ts b/src/plugins/discover/public/index.ts
index c5050147c3d5a..dbc361ee59f49 100644
--- a/src/plugins/discover/public/index.ts
+++ b/src/plugins/discover/public/index.ts
@@ -17,5 +17,18 @@
* under the License.
*/
+import { DiscoverPlugin } from './plugin';
+
+export { DiscoverSetup, DiscoverStart } from './plugin';
+export { DocViewTable } from './components/table/table';
+export { JsonCodeBlock } from './components/json_code_block/json_code_block';
+export { DocViewInput, DocViewInputFn, DocViewerComponent } from './doc_views/doc_views_types';
+export { FieldName } from './components/field_name/field_name';
+export * from './doc_views/doc_views_types';
+
+export function plugin() {
+ return new DiscoverPlugin();
+}
+
export { createSavedSearchesLoader } from './saved_searches/saved_searches';
export { SavedSearchLoader, SavedSearch } from './saved_searches/types';
diff --git a/src/legacy/core_plugins/data/index.ts b/src/plugins/discover/public/mocks.ts
similarity index 58%
rename from src/legacy/core_plugins/data/index.ts
rename to src/plugins/discover/public/mocks.ts
index 10c8cf464b82d..bb05e3d412001 100644
--- a/src/legacy/core_plugins/data/index.ts
+++ b/src/plugins/discover/public/mocks.ts
@@ -17,25 +17,31 @@
* under the License.
*/
-import { resolve } from 'path';
-import { Legacy } from '../../../../kibana';
+import { DiscoverSetup, DiscoverStart } from '.';
-// eslint-disable-next-line import/no-default-export
-export default function DataPlugin(kibana: any) {
- const config: Legacy.PluginSpecOptions = {
- id: 'data',
- require: ['elasticsearch'],
- publicDir: resolve(__dirname, 'public'),
- config: (Joi: any) => {
- return Joi.object({
- enabled: Joi.boolean().default(true),
- }).default();
+export type Setup = jest.Mocked;
+export type Start = jest.Mocked;
+
+const createSetupContract = (): Setup => {
+ const setupContract: Setup = {
+ docViews: {
+ addDocView: jest.fn(),
+ setAngularInjectorGetter: jest.fn(),
},
- init: (server: Legacy.Server) => ({}),
- uiExports: {
- injectDefaultVars: () => ({}),
+ };
+ return setupContract;
+};
+
+const createStartContract = (): Start => {
+ const startContract: Start = {
+ docViews: {
+ DocViewer: jest.fn(() => null),
},
};
+ return startContract;
+};
- return new kibana.Plugin(config);
-}
+export const discoverPluginMock = {
+ createSetupContract,
+ createStartContract,
+};
diff --git a/src/plugins/discover/public/plugin.ts b/src/plugins/discover/public/plugin.ts
new file mode 100644
index 0000000000000..d2797586bfdfb
--- /dev/null
+++ b/src/plugins/discover/public/plugin.ts
@@ -0,0 +1,110 @@
+/*
+ * 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 { i18n } from '@kbn/i18n';
+import { auto } from 'angular';
+import { CoreSetup, Plugin } from 'kibana/public';
+import { DocViewInput, DocViewInputFn, DocViewRenderProps } from './doc_views/doc_views_types';
+import { DocViewsRegistry } from './doc_views/doc_views_registry';
+import { DocViewTable } from './components/table/table';
+import { JsonCodeBlock } from './components/json_code_block/json_code_block';
+import { DocViewer } from './components/doc_viewer/doc_viewer';
+import { setDocViewsRegistry } from './services';
+
+import './index.scss';
+
+/**
+ * @public
+ */
+export interface DiscoverSetup {
+ docViews: {
+ /**
+ * Add new doc view shown along with table view and json view in the details of each document in Discover.
+ * Both react and angular doc views are supported.
+ * @param docViewRaw
+ */
+ addDocView(docViewRaw: DocViewInput | DocViewInputFn): void;
+ /**
+ * Set the angular injector for bootstrapping angular doc views. This is only exposed temporarily to aid
+ * migration to the new platform and will be removed soon.
+ * @deprecated
+ * @param injectorGetter
+ */
+ setAngularInjectorGetter(injectorGetter: () => Promise): void;
+ };
+}
+/**
+ * @public
+ */
+export interface DiscoverStart {
+ docViews: {
+ /**
+ * Component rendering all the doc views for a given document.
+ * This is only exposed temporarily to aid migration to the new platform and will be removed soon.
+ * @deprecated
+ */
+ DocViewer: React.ComponentType;
+ };
+}
+
+/**
+ * Contains Discover, one of the oldest parts of Kibana
+ * There are 2 kinds of Angular bootstrapped for rendering, additionally to the main Angular
+ * Discover provides embeddables, those contain a slimmer Angular
+ */
+export class DiscoverPlugin implements Plugin {
+ private docViewsRegistry: DocViewsRegistry | null = null;
+
+ setup(core: CoreSetup): DiscoverSetup {
+ this.docViewsRegistry = new DocViewsRegistry();
+ setDocViewsRegistry(this.docViewsRegistry);
+ this.docViewsRegistry.addDocView({
+ title: i18n.translate('discover.docViews.table.tableTitle', {
+ defaultMessage: 'Table',
+ }),
+ order: 10,
+ component: DocViewTable,
+ });
+ this.docViewsRegistry.addDocView({
+ title: i18n.translate('discover.docViews.json.jsonTitle', {
+ defaultMessage: 'JSON',
+ }),
+ order: 20,
+ component: JsonCodeBlock,
+ });
+
+ return {
+ docViews: {
+ addDocView: this.docViewsRegistry.addDocView.bind(this.docViewsRegistry),
+ setAngularInjectorGetter: this.docViewsRegistry.setAngularInjectorGetter.bind(
+ this.docViewsRegistry
+ ),
+ },
+ };
+ }
+
+ start() {
+ return {
+ docViews: {
+ DocViewer,
+ },
+ };
+ }
+}
diff --git a/src/plugins/discover/public/saved_searches/_saved_search.ts b/src/plugins/discover/public/saved_searches/_saved_search.ts
index 72983b7835eee..56360b04a49c8 100644
--- a/src/plugins/discover/public/saved_searches/_saved_search.ts
+++ b/src/plugins/discover/public/saved_searches/_saved_search.ts
@@ -16,7 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { createSavedObjectClass, SavedObjectKibanaServices } from '../../../saved_objects/public';
+import {
+ createSavedObjectClass,
+ SavedObject,
+ SavedObjectKibanaServices,
+} from '../../../saved_objects/public';
export function createSavedSearchClass(services: SavedObjectKibanaServices) {
const SavedObjectClass = createSavedObjectClass(services);
@@ -66,5 +70,5 @@ export function createSavedSearchClass(services: SavedObjectKibanaServices) {
}
}
- return SavedSearch;
+ return SavedSearch as new (id: string) => SavedObject;
}
diff --git a/src/plugins/discover/public/services.ts b/src/plugins/discover/public/services.ts
new file mode 100644
index 0000000000000..3a28759d82b71
--- /dev/null
+++ b/src/plugins/discover/public/services.ts
@@ -0,0 +1,25 @@
+/*
+ * Licensed to Elasticsearch B.V. under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch B.V. licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { createGetterSetter } from '../../kibana_utils/common';
+import { DocViewsRegistry } from './doc_views/doc_views_registry';
+
+export const [getDocViewsRegistry, setDocViewsRegistry] = createGetterSetter(
+ 'DocViewsRegistry'
+);
diff --git a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts
index 9125dc0813f98..d37cba6f897c8 100644
--- a/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts
+++ b/src/plugins/embeddable/public/lib/actions/edit_panel_action.ts
@@ -33,7 +33,7 @@ interface ActionContext {
export class EditPanelAction implements Action {
public readonly type = ACTION_EDIT_PANEL;
public readonly id = ACTION_EDIT_PANEL;
- public order = 15;
+ public order = 50;
constructor(private readonly getEmbeddableFactory: EmbeddableStart['getEmbeddableFactory']) {}
diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
index eb10c16806640..35973cc16cf9b 100644
--- a/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
+++ b/src/plugins/embeddable/public/lib/embeddables/embeddable.tsx
@@ -16,23 +16,35 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { isEqual, cloneDeep } from 'lodash';
+import { cloneDeep, isEqual } from 'lodash';
import * as Rx from 'rxjs';
-import { Adapters } from '../types';
+import { Adapters, ViewMode } from '../types';
import { IContainer } from '../containers';
-import { IEmbeddable, EmbeddableInput, EmbeddableOutput } from './i_embeddable';
-import { ViewMode } from '../types';
+import { EmbeddableInput, EmbeddableOutput, IEmbeddable } from './i_embeddable';
import { TriggerContextMapping } from '../ui_actions';
import { EmbeddableActionStorage } from './embeddable_action_storage';
+import {
+ UiActionsDynamicActionManager,
+ UiActionsStart,
+} from '../../../../../plugins/ui_actions/public';
+import { EmbeddableContext } from '../triggers';
function getPanelTitle(input: EmbeddableInput, output: EmbeddableOutput) {
return input.hidePanelTitles ? '' : input.title === undefined ? output.defaultTitle : input.title;
}
+export interface EmbeddableParams {
+ uiActions?: UiActionsStart;
+}
+
export abstract class Embeddable<
TEmbeddableInput extends EmbeddableInput = EmbeddableInput,
TEmbeddableOutput extends EmbeddableOutput = EmbeddableOutput
> implements IEmbeddable {
+ static runtimeId: number = 0;
+
+ public readonly runtimeId = Embeddable.runtimeId++;
+
public readonly parent?: IContainer;
public readonly isContainer: boolean = false;
public abstract readonly type: string;
@@ -48,15 +60,34 @@ export abstract class Embeddable<
// to update input when the parent changes.
private parentSubscription?: Rx.Subscription;
+ private storageSubscription?: Rx.Subscription;
+
// TODO: Rename to destroyed.
private destoyed: boolean = false;
- private __actionStorage?: EmbeddableActionStorage;
- public get actionStorage(): EmbeddableActionStorage {
- return this.__actionStorage || (this.__actionStorage = new EmbeddableActionStorage(this));
+ private storage = new EmbeddableActionStorage((this as unknown) as Embeddable);
+
+ private cachedDynamicActions?: UiActionsDynamicActionManager;
+ public get dynamicActions(): UiActionsDynamicActionManager | undefined {
+ if (!this.params.uiActions) return undefined;
+ if (!this.cachedDynamicActions) {
+ this.cachedDynamicActions = new UiActionsDynamicActionManager({
+ isCompatible: async (context: unknown) =>
+ (context as EmbeddableContext).embeddable.runtimeId === this.runtimeId,
+ storage: this.storage,
+ uiActions: this.params.uiActions,
+ });
+ }
+
+ return this.cachedDynamicActions;
}
- constructor(input: TEmbeddableInput, output: TEmbeddableOutput, parent?: IContainer) {
+ constructor(
+ input: TEmbeddableInput,
+ output: TEmbeddableOutput,
+ parent?: IContainer,
+ public readonly params: EmbeddableParams = {}
+ ) {
this.id = input.id;
this.output = {
title: getPanelTitle(input, output),
@@ -80,6 +111,18 @@ export abstract class Embeddable<
this.onResetInput(newInput);
});
}
+
+ if (this.dynamicActions) {
+ this.dynamicActions.start().catch(error => {
+ /* eslint-disable */
+ console.log('Failed to start embeddable dynamic actions', this);
+ console.error(error);
+ /* eslint-enable */
+ });
+ this.storageSubscription = this.input$.subscribe(() => {
+ this.storage.reload$.next();
+ });
+ }
}
public getIsContainer(): this is IContainer {
@@ -158,6 +201,20 @@ export abstract class Embeddable<
*/
public destroy(): void {
this.destoyed = true;
+
+ if (this.dynamicActions) {
+ this.dynamicActions.stop().catch(error => {
+ /* eslint-disable */
+ console.log('Failed to stop embeddable dynamic actions', this);
+ console.error(error);
+ /* eslint-enable */
+ });
+ }
+
+ if (this.storageSubscription) {
+ this.storageSubscription.unsubscribe();
+ }
+
if (this.parentSubscription) {
this.parentSubscription.unsubscribe();
}
diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts
index 56facc37fc666..83fd3f184e098 100644
--- a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts
+++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.test.ts
@@ -20,7 +20,8 @@
import { Embeddable } from './embeddable';
import { EmbeddableInput } from './i_embeddable';
import { ViewMode } from '../types';
-import { EmbeddableActionStorage, SerializedEvent } from './embeddable_action_storage';
+import { EmbeddableActionStorage } from './embeddable_action_storage';
+import { UiActionsSerializedEvent } from '../../../../ui_actions/public';
import { of } from '../../../../kibana_utils/common';
class TestEmbeddable extends Embeddable {
@@ -42,9 +43,9 @@ describe('EmbeddableActionStorage', () => {
test('can add event to embeddable', async () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event: SerializedEvent = {
+ const event: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -57,23 +58,40 @@ describe('EmbeddableActionStorage', () => {
expect(events2).toEqual([event]);
});
+ test('does not merge .getInput() into .updateInput()', async () => {
+ const embeddable = new TestEmbeddable();
+ const storage = new EmbeddableActionStorage(embeddable);
+ const event: UiActionsSerializedEvent = {
+ eventId: 'EVENT_ID',
+ triggers: ['TRIGGER-ID'],
+ action: {} as any,
+ };
+
+ const spy = jest.spyOn(embeddable, 'updateInput');
+
+ await storage.create(event);
+
+ expect(spy.mock.calls[0][0].id).toBe(undefined);
+ expect(spy.mock.calls[0][0].viewMode).toBe(undefined);
+ });
+
test('can create multiple events', async () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event1: SerializedEvent = {
+ const event1: UiActionsSerializedEvent = {
eventId: 'EVENT_ID1',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
- const event2: SerializedEvent = {
+ const event2: UiActionsSerializedEvent = {
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
- const event3: SerializedEvent = {
+ const event3: UiActionsSerializedEvent = {
eventId: 'EVENT_ID3',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -95,9 +113,9 @@ describe('EmbeddableActionStorage', () => {
test('throws when creating an event with the same ID', async () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event: SerializedEvent = {
+ const event: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -122,16 +140,16 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event1: SerializedEvent = {
+ const event1: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'foo',
} as any,
};
- const event2: SerializedEvent = {
+ const event2: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'bar',
} as any,
@@ -148,30 +166,30 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event1: SerializedEvent = {
+ const event1: UiActionsSerializedEvent = {
eventId: 'EVENT_ID1',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'foo',
} as any,
};
- const event2: SerializedEvent = {
+ const event2: UiActionsSerializedEvent = {
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'bar',
} as any,
};
- const event22: SerializedEvent = {
+ const event22: UiActionsSerializedEvent = {
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'baz',
} as any,
};
- const event3: SerializedEvent = {
+ const event3: UiActionsSerializedEvent = {
eventId: 'EVENT_ID3',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'qux',
} as any,
@@ -199,9 +217,9 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event: SerializedEvent = {
+ const event: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -217,14 +235,14 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event1: SerializedEvent = {
+ const event1: UiActionsSerializedEvent = {
eventId: 'EVENT_ID1',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
- const event2: SerializedEvent = {
+ const event2: UiActionsSerializedEvent = {
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -249,9 +267,9 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event: SerializedEvent = {
+ const event: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -266,23 +284,23 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event1: SerializedEvent = {
+ const event1: UiActionsSerializedEvent = {
eventId: 'EVENT_ID1',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'foo',
} as any,
};
- const event2: SerializedEvent = {
+ const event2: UiActionsSerializedEvent = {
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'bar',
} as any,
};
- const event3: SerializedEvent = {
+ const event3: UiActionsSerializedEvent = {
eventId: 'EVENT_ID3',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {
name: 'qux',
} as any,
@@ -327,9 +345,9 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event: SerializedEvent = {
+ const event: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -355,9 +373,9 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event: SerializedEvent = {
+ const event: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -383,9 +401,9 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event: SerializedEvent = {
+ const event: UiActionsSerializedEvent = {
eventId: 'EVENT_ID',
- triggerId: 'TRIGGER-ID',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -402,19 +420,19 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event1: SerializedEvent = {
+ const event1: UiActionsSerializedEvent = {
eventId: 'EVENT_ID1',
- triggerId: 'TRIGGER-ID1',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
- const event2: SerializedEvent = {
+ const event2: UiActionsSerializedEvent = {
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID2',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
- const event3: SerializedEvent = {
+ const event3: UiActionsSerializedEvent = {
eventId: 'EVENT_ID3',
- triggerId: 'TRIGGER-ID3',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
@@ -458,7 +476,7 @@ describe('EmbeddableActionStorage', () => {
await storage.create({
eventId: 'EVENT_ID1',
- triggerId: 'TRIGGER-ID1',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
});
@@ -466,7 +484,7 @@ describe('EmbeddableActionStorage', () => {
await storage.create({
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID1',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
});
@@ -502,15 +520,15 @@ describe('EmbeddableActionStorage', () => {
const embeddable = new TestEmbeddable();
const storage = new EmbeddableActionStorage(embeddable);
- const event1: SerializedEvent = {
+ const event1: UiActionsSerializedEvent = {
eventId: 'EVENT_ID1',
- triggerId: 'TRIGGER-ID1',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
- const event2: SerializedEvent = {
+ const event2: UiActionsSerializedEvent = {
eventId: 'EVENT_ID2',
- triggerId: 'TRIGGER-ID1',
+ triggers: ['TRIGGER-ID'],
action: {} as any,
};
diff --git a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.ts b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.ts
index 520f92840c5f9..fad5b4d535d6c 100644
--- a/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.ts
+++ b/src/plugins/embeddable/public/lib/embeddables/embeddable_action_storage.ts
@@ -17,32 +17,20 @@
* under the License.
*/
+import {
+ UiActionsAbstractActionStorage,
+ UiActionsSerializedEvent,
+} from '../../../../ui_actions/public';
import { Embeddable } from '..';
-/**
- * Below two interfaces are here temporarily, they will move to `ui_actions`
- * plugin once #58216 is merged.
- */
-export interface SerializedEvent {
- eventId: string;
- triggerId: string;
- action: unknown;
-}
-export interface ActionStorage {
- create(event: SerializedEvent): Promise;
- update(event: SerializedEvent): Promise;
- remove(eventId: string): Promise;
- read(eventId: string): Promise;
- count(): Promise;
- list(): Promise;
-}
-
-export class EmbeddableActionStorage implements ActionStorage {
- constructor(private readonly embbeddable: Embeddable) {}
+export class EmbeddableActionStorage extends UiActionsAbstractActionStorage {
+ constructor(private readonly embbeddable: Embeddable) {
+ super();
+ }
- async create(event: SerializedEvent) {
+ async create(event: UiActionsSerializedEvent) {
const input = this.embbeddable.getInput();
- const events = (input.events || []) as SerializedEvent[];
+ const events = (input.events || []) as UiActionsSerializedEvent[];
const exists = !!events.find(({ eventId }) => eventId === event.eventId);
if (exists) {
@@ -53,14 +41,13 @@ export class EmbeddableActionStorage implements ActionStorage {
}
this.embbeddable.updateInput({
- ...input,
events: [...events, event],
});
}
- async update(event: SerializedEvent) {
+ async update(event: UiActionsSerializedEvent) {
const input = this.embbeddable.getInput();
- const events = (input.events || []) as SerializedEvent[];
+ const events = (input.events || []) as UiActionsSerializedEvent[];
const index = events.findIndex(({ eventId }) => eventId === event.eventId);
if (index === -1) {
@@ -72,14 +59,13 @@ export class EmbeddableActionStorage implements ActionStorage {
}
this.embbeddable.updateInput({
- ...input,
events: [...events.slice(0, index), event, ...events.slice(index + 1)],
});
}
async remove(eventId: string) {
const input = this.embbeddable.getInput();
- const events = (input.events || []) as SerializedEvent[];
+ const events = (input.events || []) as UiActionsSerializedEvent[];
const index = events.findIndex(event => eventId === event.eventId);
if (index === -1) {
@@ -91,14 +77,13 @@ export class EmbeddableActionStorage implements ActionStorage {
}
this.embbeddable.updateInput({
- ...input,
events: [...events.slice(0, index), ...events.slice(index + 1)],
});
}
- async read(eventId: string): Promise {
+ async read(eventId: string): Promise {
const input = this.embbeddable.getInput();
- const events = (input.events || []) as SerializedEvent[];
+ const events = (input.events || []) as UiActionsSerializedEvent[];
const event = events.find(ev => eventId === ev.eventId);
if (!event) {
@@ -113,14 +98,10 @@ export class EmbeddableActionStorage implements ActionStorage {
private __list() {
const input = this.embbeddable.getInput();
- return (input.events || []) as SerializedEvent[];
- }
-
- async count(): Promise {
- return this.__list().length;
+ return (input.events || []) as UiActionsSerializedEvent[];
}
- async list(): Promise {
+ async list(): Promise {
return this.__list();
}
}
diff --git a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
index 6345c34b0dda2..9a4452aceba00 100644
--- a/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
+++ b/src/plugins/embeddable/public/lib/embeddables/i_embeddable.ts
@@ -18,6 +18,7 @@
*/
import { Observable } from 'rxjs';
+import { UiActionsDynamicActionManager } from '../../../../../plugins/ui_actions/public';
import { Adapters } from '../types';
import { IContainer } from '../containers/i_container';
import { ViewMode } from '../types';
@@ -33,7 +34,7 @@ export interface EmbeddableInput {
/**
* Reserved key for `ui_actions` events.
*/
- events?: unknown;
+ events?: Array<{ eventId: string }>;
/**
* List of action IDs that this embeddable should not render.
@@ -82,6 +83,19 @@ export interface IEmbeddable<
**/
readonly id: string;
+ /**
+ * Unique ID an embeddable is assigned each time it is initialized. This ID
+ * is different for different instances of the same embeddable. For example,
+ * if the same dashboard is rendered twice on the screen, all embeddable
+ * instances will have a unique `runtimeId`.
+ */
+ readonly runtimeId?: number;
+
+ /**
+ * Default implementation of dynamic action API for embeddables.
+ */
+ dynamicActions?: UiActionsDynamicActionManager;
+
/**
* A functional representation of the isContainer variable, but helpful for typescript to
* know the shape if this returns true
diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
index 757d4e6bfddef..83d3d5e10761b 100644
--- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
+++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.test.tsx
@@ -44,7 +44,7 @@ import {
import { inspectorPluginMock } from 'src/plugins/inspector/public/mocks';
import { EuiBadge } from '@elastic/eui';
-const actionRegistry = new Map>();
+const actionRegistry = new Map();
const triggerRegistry = new Map();
const embeddableFactories = new Map();
const getEmbeddableFactory = (id: string) => embeddableFactories.get(id);
@@ -213,13 +213,17 @@ const renderInEditModeAndOpenContextMenu = async (
};
test('HelloWorldContainer in edit mode hides disabledActions', async () => {
- const action: Action = {
+ const action = {
id: 'FOO',
type: 'FOO' as ActionType,
getIconType: () => undefined,
getDisplayName: () => 'foo',
isCompatible: async () => true,
execute: async () => {},
+ order: 10,
+ getHref: () => {
+ return undefined;
+ },
};
const getActions = () => Promise.resolve([action]);
@@ -245,13 +249,17 @@ test('HelloWorldContainer in edit mode hides disabledActions', async () => {
});
test('HelloWorldContainer hides disabled badges', async () => {
- const action: Action = {
+ const action = {
id: 'BAR',
type: 'BAR' as ActionType,
getIconType: () => undefined,
getDisplayName: () => 'bar',
isCompatible: async () => true,
execute: async () => {},
+ order: 10,
+ getHref: () => {
+ return undefined;
+ },
};
const getActions = () => Promise.resolve([action]);
diff --git a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
index b95060a73252f..c6537f2d94994 100644
--- a/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
+++ b/src/plugins/embeddable/public/lib/panel/embeddable_panel.tsx
@@ -38,6 +38,14 @@ import { EditPanelAction } from '../actions';
import { CustomizePanelModal } from './panel_header/panel_actions/customize_title/customize_panel_modal';
import { EmbeddableStart } from '../../plugin';
+const sortByOrderField = (
+ { order: orderA }: { order?: number },
+ { order: orderB }: { order?: number }
+) => (orderB || 0) - (orderA || 0);
+
+const removeById = (disabledActions: string[]) => ({ id }: { id: string }) =>
+ disabledActions.indexOf(id) === -1;
+
interface Props {
embeddable: IEmbeddable;
getActions: UiActionsService['getTriggerCompatibleActions'];
@@ -57,12 +65,14 @@ interface State {
hidePanelTitles: boolean;
closeContextMenu: boolean;
badges: Array>;
+ eventCount?: number;
}
export class EmbeddablePanel extends React.Component {
private embeddableRoot: React.RefObject;
private parentSubscription?: Subscription;
private subscription?: Subscription;
+ private eventCountSubscription?: Subscription;
private mounted: boolean = false;
private generateId = htmlIdGenerator();
@@ -136,6 +146,9 @@ export class EmbeddablePanel extends React.Component {
if (this.subscription) {
this.subscription.unsubscribe();
}
+ if (this.eventCountSubscription) {
+ this.eventCountSubscription.unsubscribe();
+ }
if (this.parentSubscription) {
this.parentSubscription.unsubscribe();
}
@@ -177,6 +190,7 @@ export class EmbeddablePanel extends React.Component {
badges={this.state.badges}
embeddable={this.props.embeddable}
headerId={headerId}
+ eventCount={this.state.eventCount}
/>
)}
@@ -188,6 +202,15 @@ export class EmbeddablePanel extends React.Component {
if (this.embeddableRoot.current) {
this.props.embeddable.render(this.embeddableRoot.current);
}
+
+ const dynamicActions = this.props.embeddable.dynamicActions;
+ if (dynamicActions) {
+ this.setState({ eventCount: dynamicActions.state.get().events.length });
+ this.eventCountSubscription = dynamicActions.state.state$.subscribe(({ events }) => {
+ if (!this.mounted) return;
+ this.setState({ eventCount: events.length });
+ });
+ }
}
closeMyContextMenuPanel = () => {
@@ -201,13 +224,14 @@ export class EmbeddablePanel extends React.Component {
};
private getActionContextMenuPanel = async () => {
- let actions = await this.props.getActions(CONTEXT_MENU_TRIGGER, {
+ let regularActions = await this.props.getActions(CONTEXT_MENU_TRIGGER, {
embeddable: this.props.embeddable,
});
const { disabledActions } = this.props.embeddable.getInput();
if (disabledActions) {
- actions = actions.filter(action => disabledActions.indexOf(action.id) === -1);
+ const removeDisabledActions = removeById(disabledActions);
+ regularActions = regularActions.filter(removeDisabledActions);
}
const createGetUserData = (overlays: OverlayStart) =>
@@ -246,16 +270,10 @@ export class EmbeddablePanel extends React.Component {
new EditPanelAction(this.props.getEmbeddableFactory),
];
- const sorted = actions
- .concat(extraActions)
- .sort((a: Action, b: Action) => {
- const bOrder = b.order || 0;
- const aOrder = a.order || 0;
- return bOrder - aOrder;
- });
+ const sortedActions = [...regularActions, ...extraActions].sort(sortByOrderField);
return await buildContextMenuForActions({
- actions: sorted,
+ actions: sortedActions,
actionContext: { embeddable: this.props.embeddable },
closeMenu: this.closeMyContextMenuPanel,
});
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts
index c0e43c0538833..36957c3b79491 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/customize_title/customize_panel_action.ts
@@ -33,15 +33,13 @@ interface ActionContext {
export class CustomizePanelTitleAction implements Action {
public readonly type = ACTION_CUSTOMIZE_PANEL;
public id = ACTION_CUSTOMIZE_PANEL;
- public order = 10;
+ public order = 40;
- constructor(private readonly getDataFromUser: GetUserData) {
- this.order = 10;
- }
+ constructor(private readonly getDataFromUser: GetUserData) {}
public getDisplayName() {
return i18n.translate('embeddableApi.customizePanel.action.displayName', {
- defaultMessage: 'Customize panel',
+ defaultMessage: 'Edit panel title',
});
}
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts
index d04f35715537c..ae9645767b267 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/inspect_panel_action.ts
@@ -31,7 +31,7 @@ interface ActionContext {
export class InspectPanelAction implements Action {
public readonly type = ACTION_INSPECT_PANEL;
public readonly id = ACTION_INSPECT_PANEL;
- public order = 10;
+ public order = 20;
constructor(private readonly inspector: InspectorStartContract) {}
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts
index ee7948f3d6a4a..a6d4128f3f106 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_actions/remove_panel_action.ts
@@ -41,7 +41,7 @@ function hasExpandedPanelInput(
export class RemovePanelAction implements Action {
public readonly type = REMOVE_PANEL_ACTION;
public readonly id = REMOVE_PANEL_ACTION;
- public order = 5;
+ public order = 1;
constructor() {}
diff --git a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx
index 99516a1d21d6f..2a856af7ae916 100644
--- a/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx
+++ b/src/plugins/embeddable/public/lib/panel/panel_header/panel_header.tsx
@@ -23,6 +23,7 @@ import {
EuiIcon,
EuiToolTip,
EuiScreenReaderOnly,
+ EuiNotificationBadge,
} from '@elastic/eui';
import classNames from 'classnames';
import React from 'react';
@@ -40,6 +41,7 @@ export interface PanelHeaderProps {
badges: Array>;
embeddable: IEmbeddable;
headerId?: string;
+ eventCount?: number;
}
function renderBadges(badges: Array>, embeddable: IEmbeddable) {
@@ -90,6 +92,7 @@ export function PanelHeader({
badges,
embeddable,
headerId,
+ eventCount,
}: PanelHeaderProps) {
const viewDescription = getViewDescription(embeddable);
const showTitle = !isViewMode || (title && !hidePanelTitles) || viewDescription !== '';
@@ -147,7 +150,11 @@ export function PanelHeader({
)}
{renderBadges(badges, embeddable)}
-
+ {!isViewMode && !!eventCount && (
+
+ {eventCount}
+
+ )}
(
}
if (!get(newPlatform.application.capabilities, route.requireUICapability)) {
- $injector.get('kbnUrl').change('/home');
+ $injector.get('$location').url('/home');
event.preventDefault();
}
}
diff --git a/src/plugins/kibana_react/public/code_editor/code_editor.tsx b/src/plugins/kibana_react/public/code_editor/code_editor.tsx
index 37707fdec2140..e8b118b804347 100644
--- a/src/plugins/kibana_react/public/code_editor/code_editor.tsx
+++ b/src/plugins/kibana_react/public/code_editor/code_editor.tsx
@@ -25,6 +25,8 @@ import { monaco } from '@kbn/ui-shared-deps/monaco';
import { LIGHT_THEME, DARK_THEME } from './editor_theme';
+import './editor.scss';
+
export interface Props {
/** Width of editor. Defaults to 100%. */
width?: string | number;
diff --git a/src/plugins/kibana_react/public/code_editor/editor.scss b/src/plugins/kibana_react/public/code_editor/editor.scss
index 86a764716bc7c..23a3e3af7e656 100644
--- a/src/plugins/kibana_react/public/code_editor/editor.scss
+++ b/src/plugins/kibana_react/public/code_editor/editor.scss
@@ -1,3 +1,3 @@
.react-monaco-editor-container .monaco-editor .inputarea:focus {
- animation: none; // Removes textarea EUI blue underline animation from EUI
+ animation: none !important; // Removes textarea EUI blue underline animation from EUI
}
diff --git a/src/plugins/kibana_utils/common/state_containers/create_state_container_react_helpers.ts b/src/plugins/kibana_utils/common/state_containers/create_state_container_react_helpers.ts
index 36903f2d7c90f..90823359359a1 100644
--- a/src/plugins/kibana_utils/common/state_containers/create_state_container_react_helpers.ts
+++ b/src/plugins/kibana_utils/common/state_containers/create_state_container_react_helpers.ts
@@ -24,15 +24,58 @@ import { Comparator, Connect, StateContainer, UnboxState } from './types';
const { useContext, useLayoutEffect, useRef, createElement: h } = React;
+/**
+ * Returns the latest state of a state container.
+ *
+ * @param container State container which state to track.
+ */
+export const useContainerState = >(
+ container: Container
+): UnboxState => useObservable(container.state$, container.get());
+
+/**
+ * Apply selector to state container to extract only needed information. Will
+ * re-render your component only when the section changes.
+ *
+ * @param container State container which state to track.
+ * @param selector Function used to pick parts of state.
+ * @param comparator Comparator function used to memoize previous result, to not
+ * re-render React component if state did not change. By default uses
+ * `fast-deep-equal` package.
+ */
+export const useContainerSelector = , Result>(
+ container: Container,
+ selector: (state: UnboxState) => Result,
+ comparator: Comparator = defaultComparator
+): Result => {
+ const { state$, get } = container;
+ const lastValueRef = useRef(get());
+ const [value, setValue] = React.useState(() => {
+ const newValue = selector(get());
+ lastValueRef.current = newValue;
+ return newValue;
+ });
+ useLayoutEffect(() => {
+ const subscription = state$.subscribe((currentState: UnboxState) => {
+ const newValue = selector(currentState);
+ if (!comparator(lastValueRef.current, newValue)) {
+ lastValueRef.current = newValue;
+ setValue(newValue);
+ }
+ });
+ return () => subscription.unsubscribe();
+ }, [state$, comparator]);
+ return value;
+};
+
export const createStateContainerReactHelpers = >() => {
const context = React.createContext(null as any);
const useContainer = (): Container => useContext(context);
const useState = (): UnboxState => {
- const { state$, get } = useContainer();
- const value = useObservable(state$, get());
- return value;
+ const container = useContainer();
+ return useContainerState(container);
};
const useTransitions: () => Container['transitions'] = () => useContainer().transitions;
@@ -41,24 +84,8 @@ export const createStateContainerReactHelpers = ) => Result,
comparator: Comparator = defaultComparator
): Result => {
- const { state$, get } = useContainer();
- const lastValueRef = useRef(get());
- const [value, setValue] = React.useState(() => {
- const newValue = selector(get());
- lastValueRef.current = newValue;
- return newValue;
- });
- useLayoutEffect(() => {
- const subscription = state$.subscribe((currentState: UnboxState) => {
- const newValue = selector(currentState);
- if (!comparator(lastValueRef.current, newValue)) {
- lastValueRef.current = newValue;
- setValue(newValue);
- }
- });
- return () => subscription.unsubscribe();
- }, [state$, comparator]);
- return value;
+ const container = useContainer();
+ return useContainerSelector(container, selector, comparator);
};
const connect: Connect> = mapStateToProp => component => props =>
diff --git a/src/plugins/kibana_utils/common/state_containers/types.ts b/src/plugins/kibana_utils/common/state_containers/types.ts
index 26a29bc470e8a..29ffa4cd486b5 100644
--- a/src/plugins/kibana_utils/common/state_containers/types.ts
+++ b/src/plugins/kibana_utils/common/state_containers/types.ts
@@ -43,7 +43,7 @@ export interface BaseStateContainer {
export interface StateContainer<
State extends BaseState,
- PureTransitions extends object,
+ PureTransitions extends object = object,
PureSelectors extends object = {}
> extends BaseStateContainer {
transitions: Readonly>;
diff --git a/src/legacy/core_plugins/vis_type_timeseries/public/legacy_imports.ts b/src/plugins/kibana_utils/index.ts
similarity index 91%
rename from src/legacy/core_plugins/vis_type_timeseries/public/legacy_imports.ts
rename to src/plugins/kibana_utils/index.ts
index 7cf0a12e8567c..14d6e52dc0465 100644
--- a/src/legacy/core_plugins/vis_type_timeseries/public/legacy_imports.ts
+++ b/src/plugins/kibana_utils/index.ts
@@ -17,5 +17,4 @@
* under the License.
*/
-// @ts-ignore
-export { timezoneProvider } from 'ui/vis/lib/timezone';
+export { createStateContainer, StateContainer, of } from './common';
diff --git a/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap b/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap
new file mode 100644
index 0000000000000..0d54d5d3e9c4a
--- /dev/null
+++ b/src/plugins/navigation/public/top_nav_menu/__snapshots__/top_nav_menu_item.test.tsx.snap
@@ -0,0 +1,19 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`TopNavMenu Should render emphasized item which should be clickable 1`] = `
+
+ Test
+
+`;
diff --git a/src/plugins/navigation/public/top_nav_menu/_index.scss b/src/plugins/navigation/public/top_nav_menu/_index.scss
index 4a0e6af3f7f70..5befe4789dd6c 100644
--- a/src/plugins/navigation/public/top_nav_menu/_index.scss
+++ b/src/plugins/navigation/public/top_nav_menu/_index.scss
@@ -1,7 +1,11 @@
.kbnTopNavMenu__wrapper {
z-index: 5;
- .kbnTopNavMenu {
- padding: $euiSizeS 0px $euiSizeXS;
+ .kbnTopNavMenu {
+ padding: $euiSizeS 0;
+
+ .kbnTopNavItemEmphasized {
+ padding: 0 $euiSizeS;
+ }
}
}
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
index 80d1a53cd417f..14ad40f13e388 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu.tsx
@@ -46,7 +46,11 @@ export function TopNavMenu(props: TopNavMenuProps) {
if (!config) return;
return config.map((menuItem: TopNavMenuData, i: number) => {
return (
-
+
);
@@ -66,6 +70,7 @@ export function TopNavMenu(props: TopNavMenuProps) {
void;
export interface TopNavMenuData {
@@ -28,6 +30,9 @@ export interface TopNavMenuData {
className?: string;
disableButton?: boolean | (() => boolean);
tooltip?: string | (() => string);
+ emphasize?: boolean;
+ iconType?: string;
+ iconSide?: ButtonIconSide;
}
export interface RegisteredTopNavMenuData extends TopNavMenuData {
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.test.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.test.tsx
index 4816ef3c95869..9ba58379c5ce1 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.test.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.test.tsx
@@ -23,6 +23,15 @@ import { TopNavMenuData } from './top_nav_menu_data';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
describe('TopNavMenu', () => {
+ const ensureMenuItemDisabled = (data: TopNavMenuData) => {
+ const component = shallowWithIntl();
+ expect(component.prop('isDisabled')).toEqual(true);
+
+ const event = { currentTarget: { value: 'a' } };
+ component.simulate('click', event);
+ expect(data.run).toHaveBeenCalledTimes(0);
+ };
+
it('Should render and click an item', () => {
const data: TopNavMenuData = {
id: 'test',
@@ -60,35 +69,62 @@ describe('TopNavMenu', () => {
expect(data.run).toHaveBeenCalled();
});
- it('Should render disabled item and it shouldnt be clickable', () => {
+ it('Should render emphasized item which should be clickable', () => {
const data: TopNavMenuData = {
id: 'test',
label: 'test',
- disableButton: true,
+ iconType: 'beaker',
+ iconSide: 'right',
+ emphasize: true,
run: jest.fn(),
};
const component = shallowWithIntl();
- expect(component.prop('isDisabled')).toEqual(true);
-
const event = { currentTarget: { value: 'a' } };
component.simulate('click', event);
- expect(data.run).toHaveBeenCalledTimes(0);
+ expect(data.run).toHaveBeenCalledTimes(1);
+ expect(component).toMatchSnapshot();
+ });
+
+ it('Should render disabled item and it shouldnt be clickable', () => {
+ ensureMenuItemDisabled({
+ id: 'test',
+ label: 'test',
+ disableButton: true,
+ run: jest.fn(),
+ });
});
it('Should render item with disable function and it shouldnt be clickable', () => {
- const data: TopNavMenuData = {
+ ensureMenuItemDisabled({
id: 'test',
label: 'test',
disableButton: () => true,
run: jest.fn(),
- };
+ });
+ });
- const component = shallowWithIntl();
- expect(component.prop('isDisabled')).toEqual(true);
+ it('Should render disabled emphasized item which shouldnt be clickable', () => {
+ ensureMenuItemDisabled({
+ id: 'test',
+ label: 'test',
+ iconType: 'beaker',
+ iconSide: 'right',
+ emphasize: true,
+ disableButton: true,
+ run: jest.fn(),
+ });
+ });
- const event = { currentTarget: { value: 'a' } };
- component.simulate('click', event);
- expect(data.run).toHaveBeenCalledTimes(0);
+ it('Should render emphasized item with disable function and it shouldnt be clickable', () => {
+ ensureMenuItemDisabled({
+ id: 'test',
+ label: 'test',
+ iconType: 'beaker',
+ iconSide: 'right',
+ emphasize: true,
+ disableButton: () => true,
+ run: jest.fn(),
+ });
});
});
diff --git a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx
index 4d3b72bae6411..92e267f17d08e 100644
--- a/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx
+++ b/src/plugins/navigation/public/top_nav_menu/top_nav_menu_item.tsx
@@ -21,6 +21,7 @@ import { capitalize, isFunction } from 'lodash';
import React, { MouseEvent } from 'react';
import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
+import { EuiButton } from '@elastic/eui';
import { TopNavMenuData } from './top_nav_menu_data';
export function TopNavMenuItem(props: TopNavMenuData) {
@@ -39,14 +40,20 @@ export function TopNavMenuItem(props: TopNavMenuData) {
props.run(e.currentTarget);
}
- const btn = (
-
+ const commonButtonProps = {
+ isDisabled: isDisabled(),
+ onClick: handleClick,
+ iconType: props.iconType,
+ iconSide: props.iconSide,
+ 'data-test-subj': props.testId,
+ };
+
+ const btn = props.emphasize ? (
+
+ {capitalize(props.label || props.id!)}
+
+ ) : (
+
{capitalize(props.label || props.id!)}
);
@@ -54,9 +61,8 @@ export function TopNavMenuItem(props: TopNavMenuData) {
const tooltip = getTooltip();
if (tooltip) {
return {btn};
- } else {
- return btn;
}
+ return btn;
}
TopNavMenuItem.defaultProps = {
diff --git a/src/legacy/core_plugins/telemetry/README.md b/src/plugins/telemetry/README.md
similarity index 81%
rename from src/legacy/core_plugins/telemetry/README.md
rename to src/plugins/telemetry/README.md
index 830c08f8e8bed..196d596fb784f 100644
--- a/src/legacy/core_plugins/telemetry/README.md
+++ b/src/plugins/telemetry/README.md
@@ -6,4 +6,4 @@ Telemetry allows Kibana features to have usage tracked in the wild. The general
2. Sending a payload of usage data up to Elastic's telemetry cluster.
3. Viewing usage data in the Kibana instance of the telemetry cluster (Viewing).
-This plugin is responsible for sending usage data to the telemetry cluster. For collecting usage data, use
+This plugin is responsible for sending usage data to the telemetry cluster. For collecting usage data, use the [`usageCollection` plugin](../usage_collection/README.md)
diff --git a/src/plugins/telemetry/common/constants.ts b/src/plugins/telemetry/common/constants.ts
index 7b7694ed9aed7..babd009143c5e 100644
--- a/src/plugins/telemetry/common/constants.ts
+++ b/src/plugins/telemetry/common/constants.ts
@@ -17,13 +17,31 @@
* under the License.
*/
+import { i18n } from '@kbn/i18n';
+
+/**
+ * config options opt into telemetry
+ */
+export const CONFIG_TELEMETRY = 'telemetry:optIn';
+
+/**
+ * config description for opting into telemetry
+ */
+export const getConfigTelemetryDesc = () => {
+ // Can't find where it's used but copying it over from the legacy code just in case...
+ return i18n.translate('telemetry.telemetryConfigDescription', {
+ defaultMessage:
+ 'Help us improve the Elastic Stack by providing usage statistics for basic features. We will not share this data outside of Elastic.',
+ });
+};
+
/**
* The amount of time, in milliseconds, to wait between reports when enabled.
* Currently 24 hours.
*/
export const REPORT_INTERVAL_MS = 86400000;
-/*
+/**
* Key for the localStorage service
*/
export const LOCALSTORAGE_KEY = 'telemetry.data';
@@ -37,3 +55,28 @@ export const PATH_TO_ADVANCED_SETTINGS = 'kibana#/management/kibana/settings';
* Link to the Elastic Telemetry privacy statement.
*/
export const PRIVACY_STATEMENT_URL = `https://www.elastic.co/legal/privacy-statement`;
+
+/**
+ * The type name used to publish telemetry plugin stats.
+ */
+export const TELEMETRY_STATS_TYPE = 'telemetry';
+
+/**
+ * The endpoint version when hitting the remote telemetry service
+ */
+export const ENDPOINT_VERSION = 'v2';
+
+/**
+ * UI metric usage type
+ */
+export const UI_METRIC_USAGE_TYPE = 'ui_metric';
+
+/**
+ * Application Usage type
+ */
+export const APPLICATION_USAGE_TYPE = 'application_usage';
+
+/**
+ * The type name used within the Monitoring index to publish management stats.
+ */
+export const KIBANA_STACK_MANAGEMENT_STATS_TYPE = 'stack_management';
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_allow_changing_opt_in_status.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_allow_changing_opt_in_status.ts
similarity index 93%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_allow_changing_opt_in_status.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_allow_changing_opt_in_status.ts
index 9fa4fbc5e0227..d7dcfd606b6ac 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_allow_changing_opt_in_status.ts
+++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_allow_changing_opt_in_status.ts
@@ -16,7 +16,8 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+
+import { TelemetrySavedObject } from './types';
interface GetTelemetryAllowChangingOptInStatus {
configTelemetryAllowChangingOptInStatus: boolean;
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_failure_details.test.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.test.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_failure_details.test.ts
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_failure_details.ts
similarity index 94%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_failure_details.ts
index 2952fa96a5cf3..c23ec42be56c4 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_failure_details.ts
+++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_failure_details.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+import { TelemetrySavedObject } from './types';
interface GetTelemetryFailureDetailsConfig {
telemetrySavedObject: TelemetrySavedObject;
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_notify_user_about_optin_default.test.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_notify_user_about_optin_default.test.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_notify_user_about_optin_default.test.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_notify_user_about_optin_default.test.ts
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_notify_user_about_optin_default.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_notify_user_about_optin_default.ts
similarity index 94%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_notify_user_about_optin_default.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_notify_user_about_optin_default.ts
index 8ef3bd8388ecb..19bd1974ffba1 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_notify_user_about_optin_default.ts
+++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_notify_user_about_optin_default.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+import { TelemetrySavedObject } from './types';
interface NotifyOpts {
allowChangingOptInStatus: boolean;
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.test.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_opt_in.test.ts
similarity index 98%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.test.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_opt_in.test.ts
index efc4a020e0ff0..da44abd35517c 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.test.ts
+++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_opt_in.test.ts
@@ -18,7 +18,7 @@
*/
import { getTelemetryOptIn } from './get_telemetry_opt_in';
-import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+import { TelemetrySavedObject } from './types';
describe('getTelemetryOptIn', () => {
it('returns null when saved object not found', () => {
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_opt_in.ts
similarity index 97%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_opt_in.ts
index d83ffdf69b576..7beb5415ad7b1 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_opt_in.ts
+++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_opt_in.ts
@@ -18,7 +18,7 @@
*/
import semver from 'semver';
-import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+import { TelemetrySavedObject } from './types';
interface GetTelemetryOptInConfig {
telemetrySavedObject: TelemetrySavedObject;
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.test.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_send_usage_from.test.ts
similarity index 96%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.test.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_send_usage_from.test.ts
index 69868a97a931d..2cf68f0abedea 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.test.ts
+++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_send_usage_from.test.ts
@@ -18,7 +18,7 @@
*/
import { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from';
-import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+import { TelemetrySavedObject } from './types';
describe('getTelemetrySendUsageFrom', () => {
it('returns kibana.yml config when saved object not found', () => {
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.ts b/src/plugins/telemetry/common/telemetry_config/get_telemetry_send_usage_from.ts
similarity index 93%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.ts
rename to src/plugins/telemetry/common/telemetry_config/get_telemetry_send_usage_from.ts
index 9e4ae14b6097c..78dc1d877c47f 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/get_telemetry_send_usage_from.ts
+++ b/src/plugins/telemetry/common/telemetry_config/get_telemetry_send_usage_from.ts
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { TelemetrySavedObject } from '../telemetry_repository/get_telemetry_saved_object';
+import { TelemetrySavedObject } from './types';
interface GetTelemetryUsageFetcherConfig {
configTelemetrySendUsageFrom: 'browser' | 'server';
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts b/src/plugins/telemetry/common/telemetry_config/index.ts
similarity index 94%
rename from src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts
rename to src/plugins/telemetry/common/telemetry_config/index.ts
index bf9855ce7538e..51eb4dd16105e 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_config/index.ts
+++ b/src/plugins/telemetry/common/telemetry_config/index.ts
@@ -17,7 +17,6 @@
* under the License.
*/
-export { replaceTelemetryInjectedVars } from './replace_injected_vars';
export { getTelemetryOptIn } from './get_telemetry_opt_in';
export { getTelemetrySendUsageFrom } from './get_telemetry_send_usage_from';
export { getTelemetryAllowChangingOptInStatus } from './get_telemetry_allow_changing_opt_in_status';
diff --git a/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts b/src/plugins/telemetry/common/telemetry_config/types.ts
similarity index 86%
rename from src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts
rename to src/plugins/telemetry/common/telemetry_config/types.ts
index f1735d1bb2866..7ab37e9544164 100644
--- a/src/legacy/core_plugins/telemetry/server/telemetry_repository/index.ts
+++ b/src/plugins/telemetry/common/telemetry_config/types.ts
@@ -17,9 +17,6 @@
* under the License.
*/
-export { getTelemetrySavedObject, TelemetrySavedObject } from './get_telemetry_saved_object';
-export { updateTelemetrySavedObject } from './update_telemetry_saved_object';
-
export interface TelemetrySavedObjectAttributes {
enabled?: boolean | null;
lastVersionChecked?: string;
@@ -30,3 +27,5 @@ export interface TelemetrySavedObjectAttributes {
reportFailureCount?: number;
reportFailureVersion?: string;
}
+
+export type TelemetrySavedObject = TelemetrySavedObjectAttributes | null | false;
diff --git a/src/plugins/telemetry/kibana.json b/src/plugins/telemetry/kibana.json
index 3a28149276c3e..f623f4f2a565d 100644
--- a/src/plugins/telemetry/kibana.json
+++ b/src/plugins/telemetry/kibana.json
@@ -1,6 +1,10 @@
{
"id": "telemetry",
"version": "kibana",
- "server": false,
- "ui": true
+ "server": true,
+ "ui": true,
+ "requiredPlugins": [
+ "telemetryCollectionManager",
+ "usageCollection"
+ ]
}
diff --git a/src/plugins/telemetry/public/components/index.ts b/src/plugins/telemetry/public/components/index.ts
index f4341154f527a..8fda181b2ed93 100644
--- a/src/plugins/telemetry/public/components/index.ts
+++ b/src/plugins/telemetry/public/components/index.ts
@@ -17,6 +17,4 @@
* under the License.
*/
-export { OptInExampleFlyout } from './opt_in_example_flyout';
-export { TelemetryManagementSection } from './telemetry_management_section';
export { OptedInNoticeBanner } from './opted_in_notice_banner';
diff --git a/src/plugins/telemetry/public/index.ts b/src/plugins/telemetry/public/index.ts
index 2f86d7749bb9b..665c89ba2bffa 100644
--- a/src/plugins/telemetry/public/index.ts
+++ b/src/plugins/telemetry/public/index.ts
@@ -17,9 +17,10 @@
* under the License.
*/
-import { TelemetryPlugin } from './plugin';
+import { PluginInitializerContext } from 'kibana/public';
+import { TelemetryPlugin, TelemetryPluginConfig } from './plugin';
export { TelemetryPluginStart, TelemetryPluginSetup } from './plugin';
-export function plugin() {
- return new TelemetryPlugin();
+export function plugin(initializerContext: PluginInitializerContext) {
+ return new TelemetryPlugin(initializerContext);
}
diff --git a/src/plugins/telemetry/public/mocks.ts b/src/plugins/telemetry/public/mocks.ts
index 93dc13c327509..4e0f02242961a 100644
--- a/src/plugins/telemetry/public/mocks.ts
+++ b/src/plugins/telemetry/public/mocks.ts
@@ -23,8 +23,6 @@ import { overlayServiceMock } from '../../../core/public/overlays/overlay_servic
import { httpServiceMock } from '../../../core/public/http/http_service.mock';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { notificationServiceMock } from '../../../core/public/notifications/notifications_service.mock';
-// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { injectedMetadataServiceMock } from '../../../core/public/injected_metadata/injected_metadata_service.mock';
import { TelemetryService } from './services/telemetry_service';
import { TelemetryNotifications } from './services/telemetry_notifications/telemetry_notifications';
import { TelemetryPluginStart } from './plugin';
@@ -32,23 +30,19 @@ import { TelemetryPluginStart } from './plugin';
export function mockTelemetryService({
reportOptInStatusChange,
}: { reportOptInStatusChange?: boolean } = {}) {
- const injectedMetadata = injectedMetadataServiceMock.createStartContract();
- injectedMetadata.getInjectedVar.mockImplementation((key: string) => {
- switch (key) {
- case 'telemetryNotifyUserAboutOptInDefault':
- return true;
- case 'allowChangingOptInStatus':
- return true;
- case 'telemetryOptedIn':
- return true;
- default: {
- throw Error(`Unhandled getInjectedVar key "${key}".`);
- }
- }
- });
+ const config = {
+ enabled: true,
+ url: 'http://localhost',
+ optInStatusUrl: 'http://localhost',
+ sendUsageFrom: 'browser' as const,
+ optIn: true,
+ banner: true,
+ allowChangingOptInStatus: true,
+ telemetryNotifyUserAboutOptInDefault: true,
+ };
return new TelemetryService({
- injectedMetadata,
+ config,
http: httpServiceMock.createStartContract(),
notifications: notificationServiceMock.createStartContract(),
reportOptInStatusChange,
diff --git a/src/plugins/telemetry/public/plugin.ts b/src/plugins/telemetry/public/plugin.ts
index 9cfb4ca1ec395..86679227059e6 100644
--- a/src/plugins/telemetry/public/plugin.ts
+++ b/src/plugins/telemetry/public/plugin.ts
@@ -16,9 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
-import { Plugin, CoreStart, CoreSetup, HttpStart } from '../../../core/public';
+
+import {
+ Plugin,
+ CoreStart,
+ CoreSetup,
+ HttpStart,
+ PluginInitializerContext,
+ SavedObjectsClientContract,
+} from '../../../core/public';
import { TelemetrySender, TelemetryService, TelemetryNotifications } from './services';
+import {
+ TelemetrySavedObjectAttributes,
+ TelemetrySavedObject,
+} from '../common/telemetry_config/types';
+import {
+ getTelemetryAllowChangingOptInStatus,
+ getTelemetryOptIn,
+ getTelemetrySendUsageFrom,
+} from '../common/telemetry_config';
+import { getNotifyUserAboutOptInDefault } from '../common/telemetry_config/get_telemetry_notify_user_about_optin_default';
export interface TelemetryPluginSetup {
telemetryService: TelemetryService;
@@ -29,17 +47,32 @@ export interface TelemetryPluginStart {
telemetryNotifications: TelemetryNotifications;
}
+export interface TelemetryPluginConfig {
+ enabled: boolean;
+ url: string;
+ banner: boolean;
+ allowChangingOptInStatus: boolean;
+ optIn: boolean | null;
+ optInStatusUrl: string;
+ sendUsageFrom: 'browser' | 'server';
+ telemetryNotifyUserAboutOptInDefault?: boolean;
+}
+
export class TelemetryPlugin implements Plugin {
+ private readonly currentKibanaVersion: string;
+ private readonly config: TelemetryPluginConfig;
private telemetrySender?: TelemetrySender;
private telemetryNotifications?: TelemetryNotifications;
private telemetryService?: TelemetryService;
- public setup({ http, injectedMetadata, notifications }: CoreSetup): TelemetryPluginSetup {
- this.telemetryService = new TelemetryService({
- http,
- injectedMetadata,
- notifications,
- });
+ constructor(initializerContext: PluginInitializerContext) {
+ this.currentKibanaVersion = initializerContext.env.packageInfo.version;
+ this.config = initializerContext.config.get();
+ }
+
+ public setup({ http, notifications }: CoreSetup): TelemetryPluginSetup {
+ const config = this.config;
+ this.telemetryService = new TelemetryService({ config, http, notifications });
this.telemetrySender = new TelemetrySender(this.telemetryService);
@@ -48,24 +81,29 @@ export class TelemetryPlugin implements Plugin {
+ application.currentAppId$.subscribe(async () => {
const isUnauthenticated = this.getIsUnauthenticated(http);
if (isUnauthenticated) {
return;
}
+ // Update the telemetry config based as a mix of the config files and saved objects
+ const telemetrySavedObject = await this.getTelemetrySavedObject(savedObjects.client);
+ const updatedConfig = await this.updateConfigsBasedOnSavedObjects(telemetrySavedObject);
+ this.telemetryService!.config = updatedConfig;
+
+ const telemetryBanner = updatedConfig.banner;
+
this.maybeStartTelemetryPoller();
if (telemetryBanner) {
this.maybeShowOptedInNotificationBanner();
@@ -111,4 +149,66 @@ export class TelemetryPlugin implements Plugin {
+ const configTelemetrySendUsageFrom = this.config.sendUsageFrom;
+ const configTelemetryOptIn = this.config.optIn as boolean;
+ const configTelemetryAllowChangingOptInStatus = this.config.allowChangingOptInStatus;
+
+ const currentKibanaVersion = this.currentKibanaVersion;
+
+ const allowChangingOptInStatus = getTelemetryAllowChangingOptInStatus({
+ configTelemetryAllowChangingOptInStatus,
+ telemetrySavedObject,
+ });
+
+ const optIn = getTelemetryOptIn({
+ configTelemetryOptIn,
+ allowChangingOptInStatus,
+ telemetrySavedObject,
+ currentKibanaVersion,
+ });
+
+ const sendUsageFrom = getTelemetrySendUsageFrom({
+ configTelemetrySendUsageFrom,
+ telemetrySavedObject,
+ });
+
+ const telemetryNotifyUserAboutOptInDefault = getNotifyUserAboutOptInDefault({
+ telemetrySavedObject,
+ allowChangingOptInStatus,
+ configTelemetryOptIn,
+ telemetryOptedIn: optIn,
+ });
+
+ return {
+ ...this.config,
+ optIn,
+ sendUsageFrom,
+ telemetryNotifyUserAboutOptInDefault,
+ };
+ }
+
+ private async getTelemetrySavedObject(savedObjectsClient: SavedObjectsClientContract) {
+ try {
+ const { attributes } = await savedObjectsClient.get(
+ 'telemetry',
+ 'telemetry'
+ );
+ return attributes;
+ } catch (error) {
+ const errorCode = error[Symbol('SavedObjectsClientErrorCode')];
+ if (errorCode === 'SavedObjectsClient/notFound') {
+ return null;
+ }
+
+ if (errorCode === 'SavedObjectsClient/forbidden') {
+ return false;
+ }
+
+ throw error;
+ }
+ }
}
diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts
index cb91451bd8ef4..cac4e3fdf5f50 100644
--- a/src/plugins/telemetry/public/services/telemetry_service.ts
+++ b/src/plugins/telemetry/public/services/telemetry_service.ts
@@ -20,62 +20,75 @@
import moment from 'moment';
import { i18n } from '@kbn/i18n';
import { CoreStart } from 'kibana/public';
+import { TelemetryPluginConfig } from '../plugin';
interface TelemetryServiceConstructor {
+ config: TelemetryPluginConfig;
http: CoreStart['http'];
- injectedMetadata: CoreStart['injectedMetadata'];
notifications: CoreStart['notifications'];
reportOptInStatusChange?: boolean;
}
export class TelemetryService {
private readonly http: CoreStart['http'];
- private readonly injectedMetadata: CoreStart['injectedMetadata'];
private readonly reportOptInStatusChange: boolean;
private readonly notifications: CoreStart['notifications'];
- private isOptedIn: boolean | null;
- private userHasSeenOptedInNotice: boolean;
+ private readonly defaultConfig: TelemetryPluginConfig;
+ private updatedConfig?: TelemetryPluginConfig;
constructor({
+ config,
http,
- injectedMetadata,
notifications,
reportOptInStatusChange = true,
}: TelemetryServiceConstructor) {
- const isOptedIn = injectedMetadata.getInjectedVar('telemetryOptedIn') as boolean | null;
- const userHasSeenOptedInNotice = injectedMetadata.getInjectedVar(
- 'telemetryNotifyUserAboutOptInDefault'
- ) as boolean;
+ this.defaultConfig = config;
this.reportOptInStatusChange = reportOptInStatusChange;
- this.injectedMetadata = injectedMetadata;
this.notifications = notifications;
this.http = http;
+ }
+
+ public set config(updatedConfig: TelemetryPluginConfig) {
+ this.updatedConfig = updatedConfig;
+ }
+
+ public get config() {
+ return { ...this.defaultConfig, ...this.updatedConfig };
+ }
+
+ public get isOptedIn() {
+ return this.config.optIn;
+ }
+
+ public set isOptedIn(optIn) {
+ this.config = { ...this.config, optIn };
+ }
+
+ public get userHasSeenOptedInNotice() {
+ return this.config.telemetryNotifyUserAboutOptInDefault;
+ }
- this.isOptedIn = isOptedIn;
- this.userHasSeenOptedInNotice = userHasSeenOptedInNotice;
+ public set userHasSeenOptedInNotice(telemetryNotifyUserAboutOptInDefault) {
+ this.config = { ...this.config, telemetryNotifyUserAboutOptInDefault };
}
public getCanChangeOptInStatus = () => {
- const allowChangingOptInStatus = this.injectedMetadata.getInjectedVar(
- 'allowChangingOptInStatus'
- ) as boolean;
+ const allowChangingOptInStatus = this.config.allowChangingOptInStatus;
return allowChangingOptInStatus;
};
public getOptInStatusUrl = () => {
- const telemetryOptInStatusUrl = this.injectedMetadata.getInjectedVar(
- 'telemetryOptInStatusUrl'
- ) as string;
+ const telemetryOptInStatusUrl = this.config.optInStatusUrl;
return telemetryOptInStatusUrl;
};
public getTelemetryUrl = () => {
- const telemetryUrl = this.injectedMetadata.getInjectedVar('telemetryUrl') as string;
+ const telemetryUrl = this.config.url;
return telemetryUrl;
};
public getUserHasSeenOptedInNotice = () => {
- return this.userHasSeenOptedInNotice;
+ return this.config.telemetryNotifyUserAboutOptInDefault || false;
};
public getIsOptedIn = () => {
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/application_usage/index.test.ts b/src/plugins/telemetry/server/collectors/application_usage/index.test.ts
similarity index 91%
rename from src/legacy/core_plugins/telemetry/server/collectors/application_usage/index.test.ts
rename to src/plugins/telemetry/server/collectors/application_usage/index.test.ts
index 1a64100bda692..5a8fa71363ba7 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/application_usage/index.test.ts
+++ b/src/plugins/telemetry/server/collectors/application_usage/index.test.ts
@@ -17,10 +17,10 @@
* under the License.
*/
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
-import { savedObjectsRepositoryMock } from '../../../../../../core/server/mocks';
+import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/server';
+import { savedObjectsRepositoryMock } from '../../../../../core/server/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { CollectorOptions } from '../../../../../../plugins/usage_collection/server/collector/collector';
+import { CollectorOptions } from '../../../../../plugins/usage_collection/server/collector/collector';
import { registerApplicationUsageCollector } from './';
import {
@@ -40,9 +40,12 @@ describe('telemetry_application_usage', () => {
} as any;
const getUsageCollector = jest.fn();
+ const registerType = jest.fn();
const callCluster = jest.fn();
- beforeAll(() => registerApplicationUsageCollector(usageCollectionMock, getUsageCollector));
+ beforeAll(() =>
+ registerApplicationUsageCollector(usageCollectionMock, registerType, getUsageCollector)
+ );
afterAll(() => jest.clearAllTimers());
test('registered collector is set', () => {
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/application_usage/index.ts b/src/plugins/telemetry/server/collectors/application_usage/index.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/application_usage/index.ts
rename to src/plugins/telemetry/server/collectors/application_usage/index.ts
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
new file mode 100644
index 0000000000000..9f997ab7b5df3
--- /dev/null
+++ b/src/plugins/telemetry/server/collectors/application_usage/saved_objects_types.ts
@@ -0,0 +1,59 @@
+/*
+ * 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 { SavedObjectAttributes, SavedObjectsServiceSetup } from 'kibana/server';
+
+export interface ApplicationUsageTotal extends SavedObjectAttributes {
+ appId: string;
+ minutesOnScreen: number;
+ numberOfClicks: number;
+}
+
+export interface ApplicationUsageTransactional extends ApplicationUsageTotal {
+ timestamp: string;
+}
+
+export function registerMappings(registerType: SavedObjectsServiceSetup['registerType']) {
+ registerType({
+ name: 'application_usage_totals',
+ hidden: false,
+ namespaceAgnostic: true,
+ mappings: {
+ properties: {
+ appId: { type: 'keyword' },
+ numberOfClicks: { type: 'long' },
+ minutesOnScreen: { type: 'float' },
+ },
+ },
+ });
+
+ registerType({
+ name: 'application_usage_transactional',
+ hidden: false,
+ namespaceAgnostic: true,
+ mappings: {
+ properties: {
+ timestamp: { type: 'date' },
+ appId: { type: 'keyword' },
+ numberOfClicks: { type: 'long' },
+ minutesOnScreen: { type: 'float' },
+ },
+ },
+ });
+}
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/application_usage/telemetry_application_usage_collector.ts b/src/plugins/telemetry/server/collectors/application_usage/telemetry_application_usage_collector.ts
similarity index 94%
rename from src/legacy/core_plugins/telemetry/server/collectors/application_usage/telemetry_application_usage_collector.ts
rename to src/plugins/telemetry/server/collectors/application_usage/telemetry_application_usage_collector.ts
index 5c862686a37d9..f52687038bbbc 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/application_usage/telemetry_application_usage_collector.ts
+++ b/src/plugins/telemetry/server/collectors/application_usage/telemetry_application_usage_collector.ts
@@ -18,10 +18,15 @@
*/
import moment from 'moment';
+import { ISavedObjectsRepository, SavedObjectsServiceSetup } from 'kibana/server';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { APPLICATION_USAGE_TYPE } from '../../../common/constants';
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
-import { ISavedObjectsRepository, SavedObjectAttributes } from '../../../../../../core/server';
import { findAll } from '../find_all';
+import {
+ ApplicationUsageTotal,
+ ApplicationUsageTransactional,
+ registerMappings,
+} from './saved_objects_types';
/**
* Roll indices every 24h
@@ -36,16 +41,6 @@ export const ROLL_INDICES_START = 5 * 60 * 1000;
export const SAVED_OBJECTS_TOTAL_TYPE = 'application_usage_totals';
export const SAVED_OBJECTS_TRANSACTIONAL_TYPE = 'application_usage_transactional';
-interface ApplicationUsageTotal extends SavedObjectAttributes {
- appId: string;
- minutesOnScreen: number;
- numberOfClicks: number;
-}
-
-interface ApplicationUsageTransactional extends ApplicationUsageTotal {
- timestamp: string;
-}
-
interface ApplicationUsageTelemetryReport {
[appId: string]: {
clicks_total: number;
@@ -61,8 +56,11 @@ interface ApplicationUsageTelemetryReport {
export function registerApplicationUsageCollector(
usageCollection: UsageCollectionSetup,
+ registerType: SavedObjectsServiceSetup['registerType'],
getSavedObjectsClient: () => ISavedObjectsRepository | undefined
) {
+ registerMappings(registerType);
+
const collector = usageCollection.makeUsageCollector({
type: APPLICATION_USAGE_TYPE,
isReady: () => typeof getSavedObjectsClient() !== 'undefined',
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/find_all.test.ts b/src/plugins/telemetry/server/collectors/find_all.test.ts
similarity index 96%
rename from src/legacy/core_plugins/telemetry/server/collectors/find_all.test.ts
rename to src/plugins/telemetry/server/collectors/find_all.test.ts
index 012cda395bc6c..a62c74c0c0838 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/find_all.test.ts
+++ b/src/plugins/telemetry/server/collectors/find_all.test.ts
@@ -17,7 +17,7 @@
* under the License.
*/
-import { savedObjectsRepositoryMock } from '../../../../../core/server/mocks';
+import { savedObjectsRepositoryMock } from '../../../../core/server/mocks';
import { findAll } from './find_all';
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/find_all.ts b/src/plugins/telemetry/server/collectors/find_all.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/find_all.ts
rename to src/plugins/telemetry/server/collectors/find_all.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/index.ts b/src/plugins/telemetry/server/collectors/index.ts
similarity index 90%
rename from src/legacy/core_plugins/telemetry/server/collectors/index.ts
rename to src/plugins/telemetry/server/collectors/index.ts
index 6cb7a38b6414f..6eeda080bb3ab 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/index.ts
+++ b/src/plugins/telemetry/server/collectors/index.ts
@@ -17,10 +17,8 @@
* under the License.
*/
-export { encryptTelemetry } from './encryption';
export { registerTelemetryUsageCollector } from './usage';
export { registerUiMetricUsageCollector } from './ui_metric';
-export { registerLocalizationUsageCollector } from './localization';
export { registerTelemetryPluginUsageCollector } from './telemetry_plugin';
export { registerManagementUsageCollector } from './management';
export { registerApplicationUsageCollector } from './application_usage';
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/management/index.ts b/src/plugins/telemetry/server/collectors/management/index.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/management/index.ts
rename to src/plugins/telemetry/server/collectors/management/index.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/management/telemetry_management_collector.ts b/src/plugins/telemetry/server/collectors/management/telemetry_management_collector.ts
similarity index 73%
rename from src/legacy/core_plugins/telemetry/server/collectors/management/telemetry_management_collector.ts
rename to src/plugins/telemetry/server/collectors/management/telemetry_management_collector.ts
index 481b1e9af2a79..7dc4ca64e6bc3 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/management/telemetry_management_collector.ts
+++ b/src/plugins/telemetry/server/collectors/management/telemetry_management_collector.ts
@@ -17,11 +17,10 @@
* under the License.
*/
-import { Server } from 'hapi';
import { size } from 'lodash';
+import { IUiSettingsClient } from 'kibana/server';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { KIBANA_STACK_MANAGEMENT_STATS_TYPE } from '../../../common/constants';
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
-import { SavedObjectsClient } from '../../../../../../core/server';
export type UsageStats = Record;
@@ -30,12 +29,12 @@ export async function getTranslationCount(loader: any, locale: string): Promise<
return size(translations.messages);
}
-export function createCollectorFetch(server: Server) {
- return async function fetchUsageStats(): Promise {
- const internalRepo = server.newPlatform.start.core.savedObjects.createInternalRepository();
- const uiSettingsClient = server.newPlatform.start.core.uiSettings.asScopedToClient(
- new SavedObjectsClient(internalRepo)
- );
+export function createCollectorFetch(getUiSettingsClient: () => IUiSettingsClient | undefined) {
+ return async function fetchUsageStats(): Promise {
+ const uiSettingsClient = getUiSettingsClient();
+ if (!uiSettingsClient) {
+ return;
+ }
const user = await uiSettingsClient.getUserProvided();
const modifiedEntries = Object.keys(user)
@@ -51,12 +50,12 @@ export function createCollectorFetch(server: Server) {
export function registerManagementUsageCollector(
usageCollection: UsageCollectionSetup,
- server: any
+ getUiSettingsClient: () => IUiSettingsClient | undefined
) {
const collector = usageCollection.makeUsageCollector({
type: KIBANA_STACK_MANAGEMENT_STATS_TYPE,
- isReady: () => true,
- fetch: createCollectorFetch(server),
+ isReady: () => typeof getUiSettingsClient() !== 'undefined',
+ fetch: createCollectorFetch(getUiSettingsClient),
});
usageCollection.registerCollector(collector);
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/index.ts b/src/plugins/telemetry/server/collectors/telemetry_plugin/index.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/index.ts
rename to src/plugins/telemetry/server/collectors/telemetry_plugin/index.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts b/src/plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts
similarity index 62%
rename from src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts
rename to src/plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts
index 5e25538cbad80..ab90935266d69 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts
+++ b/src/plugins/telemetry/server/collectors/telemetry_plugin/telemetry_plugin_collector.ts
@@ -17,10 +17,14 @@
* under the License.
*/
+import { Observable } from 'rxjs';
+import { take } from 'rxjs/operators';
+import { ISavedObjectsRepository, SavedObjectsClient } from '../../../../../core/server';
import { TELEMETRY_STATS_TYPE } from '../../../common/constants';
import { getTelemetrySavedObject, TelemetrySavedObject } from '../../telemetry_repository';
-import { getTelemetryOptIn, getTelemetrySendUsageFrom } from '../../telemetry_config';
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
+import { getTelemetryOptIn, getTelemetrySendUsageFrom } from '../../../common/telemetry_config';
+import { UsageCollectionSetup } from '../../../../usage_collection/server';
+import { TelemetryConfigType } from '../../config';
export interface TelemetryUsageStats {
opt_in_status?: boolean | null;
@@ -28,21 +32,31 @@ export interface TelemetryUsageStats {
last_reported?: number;
}
-export function createCollectorFetch(server: any) {
+export interface TelemetryPluginUsageCollectorOptions {
+ currentKibanaVersion: string;
+ config$: Observable;
+ getSavedObjectsClient: () => ISavedObjectsRepository | undefined;
+}
+
+export function createCollectorFetch({
+ currentKibanaVersion,
+ config$,
+ getSavedObjectsClient,
+}: TelemetryPluginUsageCollectorOptions) {
return async function fetchUsageStats(): Promise {
- const config = server.config();
- const configTelemetrySendUsageFrom = config.get('telemetry.sendUsageFrom');
- const allowChangingOptInStatus = config.get('telemetry.allowChangingOptInStatus');
- const configTelemetryOptIn = config.get('telemetry.optIn');
- const currentKibanaVersion = config.get('pkg.version');
+ const { sendUsageFrom, allowChangingOptInStatus, optIn = null } = await config$
+ .pipe(take(1))
+ .toPromise();
+ const configTelemetrySendUsageFrom = sendUsageFrom;
+ const configTelemetryOptIn = optIn;
let telemetrySavedObject: TelemetrySavedObject = {};
try {
- const { getSavedObjectsRepository } = server.savedObjects;
- const { callWithInternalUser } = server.plugins.elasticsearch.getCluster('admin');
- const internalRepository = getSavedObjectsRepository(callWithInternalUser);
- telemetrySavedObject = await getTelemetrySavedObject(internalRepository);
+ const internalRepository = getSavedObjectsClient()!;
+ telemetrySavedObject = await getTelemetrySavedObject(
+ new SavedObjectsClient(internalRepository)
+ );
} catch (err) {
// no-op
}
@@ -65,12 +79,12 @@ export function createCollectorFetch(server: any) {
export function registerTelemetryPluginUsageCollector(
usageCollection: UsageCollectionSetup,
- server: any
+ options: TelemetryPluginUsageCollectorOptions
) {
const collector = usageCollection.makeUsageCollector({
type: TELEMETRY_STATS_TYPE,
- isReady: () => true,
- fetch: createCollectorFetch(server),
+ isReady: () => typeof options.getSavedObjectsClient() !== 'undefined',
+ fetch: createCollectorFetch(options),
});
usageCollection.registerCollector(collector);
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/ui_metric/index.test.ts b/src/plugins/telemetry/server/collectors/ui_metric/index.test.ts
similarity index 87%
rename from src/legacy/core_plugins/telemetry/server/collectors/ui_metric/index.test.ts
rename to src/plugins/telemetry/server/collectors/ui_metric/index.test.ts
index ddb58a7d09bbd..d6667a6384a1f 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/ui_metric/index.test.ts
+++ b/src/plugins/telemetry/server/collectors/ui_metric/index.test.ts
@@ -17,10 +17,10 @@
* under the License.
*/
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
-import { savedObjectsRepositoryMock } from '../../../../../../core/server/mocks';
+import { UsageCollectionSetup } from '../../../../../plugins/usage_collection/server';
+import { savedObjectsRepositoryMock } from '../../../../../core/server/mocks';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
-import { CollectorOptions } from '../../../../../../plugins/usage_collection/server/collector/collector';
+import { CollectorOptions } from '../../../../../plugins/usage_collection/server/collector/collector';
import { registerUiMetricUsageCollector } from './';
@@ -33,9 +33,12 @@ describe('telemetry_ui_metric', () => {
} as any;
const getUsageCollector = jest.fn();
+ const registerType = jest.fn();
const callCluster = jest.fn();
- beforeAll(() => registerUiMetricUsageCollector(usageCollectionMock, getUsageCollector));
+ beforeAll(() =>
+ registerUiMetricUsageCollector(usageCollectionMock, registerType, getUsageCollector)
+ );
test('registered collector is set', () => {
expect(collector).not.toBeUndefined();
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/ui_metric/index.ts b/src/plugins/telemetry/server/collectors/ui_metric/index.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/ui_metric/index.ts
rename to src/plugins/telemetry/server/collectors/ui_metric/index.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts b/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
similarity index 82%
rename from src/legacy/core_plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
rename to src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
index a7b6850b0b20a..3f6e1836cac7d 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
+++ b/src/plugins/telemetry/server/collectors/ui_metric/telemetry_ui_metric_collector.ts
@@ -17,9 +17,13 @@
* under the License.
*/
-import { ISavedObjectsRepository, SavedObjectAttributes } from 'kibana/server';
+import {
+ ISavedObjectsRepository,
+ SavedObjectAttributes,
+ SavedObjectsServiceSetup,
+} from 'kibana/server';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { UI_METRIC_USAGE_TYPE } from '../../../common/constants';
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
import { findAll } from '../find_all';
interface UIMetricsSavedObjects extends SavedObjectAttributes {
@@ -28,8 +32,22 @@ interface UIMetricsSavedObjects extends SavedObjectAttributes {
export function registerUiMetricUsageCollector(
usageCollection: UsageCollectionSetup,
+ registerType: SavedObjectsServiceSetup['registerType'],
getSavedObjectsClient: () => ISavedObjectsRepository | undefined
) {
+ registerType({
+ name: 'ui-metric',
+ hidden: false,
+ namespaceAgnostic: true,
+ mappings: {
+ properties: {
+ count: {
+ type: 'integer',
+ },
+ },
+ },
+ });
+
const collector = usageCollection.makeUsageCollector({
type: UI_METRIC_USAGE_TYPE,
fetch: async () => {
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.test.ts b/src/plugins/telemetry/server/collectors/usage/ensure_deep_object.test.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.test.ts
rename to src/plugins/telemetry/server/collectors/usage/ensure_deep_object.test.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.ts b/src/plugins/telemetry/server/collectors/usage/ensure_deep_object.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/usage/ensure_deep_object.ts
rename to src/plugins/telemetry/server/collectors/usage/ensure_deep_object.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/index.ts b/src/plugins/telemetry/server/collectors/usage/index.ts
similarity index 100%
rename from src/legacy/core_plugins/telemetry/server/collectors/usage/index.ts
rename to src/plugins/telemetry/server/collectors/usage/index.ts
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts
similarity index 89%
rename from src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts
rename to src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts
index 78685cd6becc8..f44603f4f19f4 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts
+++ b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.test.ts
@@ -18,7 +18,6 @@
*/
import { writeFileSync, unlinkSync } from 'fs';
-import { Server } from 'hapi';
import { resolve } from 'path';
import { tmpdir } from 'os';
import {
@@ -32,20 +31,6 @@ const mockUsageCollector = () => ({
makeUsageCollector: jest.fn().mockImplementationOnce((arg: object) => arg),
});
-const serverWithConfig = (configPath: string): Server => {
- return {
- config: () => ({
- get: (key: string) => {
- if (key !== 'telemetry.config' && key !== 'xpack.xpack_main.telemetry.config') {
- throw new Error('Expected `telemetry.config`');
- }
-
- return configPath;
- },
- }),
- } as Server;
-};
-
describe('telemetry_usage_collector', () => {
const tempDir = tmpdir();
const tempFiles = {
@@ -129,11 +114,13 @@ describe('telemetry_usage_collector', () => {
// note: it uses the file's path to get the directory, then looks for 'telemetry.yml'
// exclusively, which is indirectly tested by passing it the wrong "file" in the same
// dir
- const server: Server = serverWithConfig(tempFiles.unreadable);
// the `makeUsageCollector` is mocked above to return the argument passed to it
const usageCollector = mockUsageCollector() as any;
- const collectorOptions = createTelemetryUsageCollector(usageCollector, server);
+ const collectorOptions = createTelemetryUsageCollector(
+ usageCollector,
+ () => tempFiles.unreadable
+ );
expect(collectorOptions.type).toBe('static_telemetry');
expect(await collectorOptions.fetch({} as any)).toEqual(expectedObject); // Sending any as the callCluster client because it's not needed in this collector but TS requires it when calling it.
diff --git a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts
similarity index 87%
rename from src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts
rename to src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts
index 6919b6959aa8c..3daae90106e9e 100644
--- a/src/legacy/core_plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts
+++ b/src/plugins/telemetry/server/collectors/usage/telemetry_usage_collector.ts
@@ -18,13 +18,16 @@
*/
import { accessSync, constants, readFileSync, statSync } from 'fs';
-import { Server } from 'hapi';
import { safeLoad } from 'js-yaml';
import { dirname, join } from 'path';
+import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
+
+// eslint-disable-next-line @kbn/eslint/no-restricted-paths
+import { getConfigPath } from '../../../../../core/server/path';
+
// look for telemetry.yml in the same places we expect kibana.yml
import { ensureDeepObject } from './ensure_deep_object';
-import { UsageCollectionSetup } from '../../../../../../plugins/usage_collection/server';
/**
* The maximum file size before we ignore it (note: this limit is arbitrary).
@@ -77,24 +80,20 @@ export async function readTelemetryFile(path: string): Promise