mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
remove inspector from Vis (#24112)
This commit is contained in:
parent
830e149787
commit
23cd191d39
9 changed files with 253 additions and 258 deletions
|
@ -212,13 +212,13 @@ function VisEditor(
|
|||
description: 'Open Inspector for visualization',
|
||||
testId: 'openInspectorButton',
|
||||
disableButton() {
|
||||
return !vis.hasInspector();
|
||||
return !vis.hasInspector || !vis.hasInspector();
|
||||
},
|
||||
run() {
|
||||
vis.openInspector().bindToAngularScope($scope);
|
||||
},
|
||||
tooltip() {
|
||||
if (!vis.hasInspector()) {
|
||||
if (!vis.hasInspector || !vis.hasInspector()) {
|
||||
return 'This visualization doesn\'t support any inspectors.';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,10 @@ export class VisualizeEmbeddable extends Embeddable {
|
|||
};
|
||||
|
||||
getInspectorAdapters() {
|
||||
return this.savedVisualization.vis.API.inspectorAdapters;
|
||||
if (!this.handler) {
|
||||
return undefined;
|
||||
}
|
||||
return this.handler.inspectorAdapters;
|
||||
}
|
||||
|
||||
getEmbeddableState() {
|
||||
|
|
57
src/ui/public/inspector/build_tabular_inspector_data.js
Normal file
57
src/ui/public/inspector/build_tabular_inspector_data.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 { FormattedData } from './adapters/data';
|
||||
|
||||
/**
|
||||
* This function builds tabular data from the response and attaches it to the
|
||||
* inspector. It will only be called when the data view in the inspector is opened.
|
||||
*/
|
||||
export async function buildTabularInspectorData(table, queryFilter) {
|
||||
const columns = table.columns.map((col, index) => {
|
||||
const field = col.aggConfig.getField();
|
||||
const isCellContentFilterable =
|
||||
col.aggConfig.isFilterable()
|
||||
&& (!field || field.filterable);
|
||||
return ({
|
||||
name: col.name,
|
||||
field: `col${index}`,
|
||||
filter: isCellContentFilterable && ((value) => {
|
||||
const filter = col.aggConfig.createFilter(value.raw);
|
||||
queryFilter.addFilters(filter);
|
||||
}),
|
||||
filterOut: isCellContentFilterable && ((value) => {
|
||||
const filter = col.aggConfig.createFilter(value.raw);
|
||||
filter.meta = filter.meta || {};
|
||||
filter.meta.negate = true;
|
||||
queryFilter.addFilters(filter);
|
||||
}),
|
||||
});
|
||||
});
|
||||
const rows = table.rows.map(row => {
|
||||
return table.columns.reduce((prev, cur, index) => {
|
||||
const value = row[cur.id];
|
||||
const fieldFormatter = cur.aggConfig.fieldFormatter('text');
|
||||
prev[`col${index}`] = new FormattedData(value, fieldFormatter(value));
|
||||
return prev;
|
||||
}, {});
|
||||
});
|
||||
|
||||
return { columns, rows };
|
||||
}
|
|
@ -24,8 +24,6 @@ import expect from 'expect.js';
|
|||
import { VisProvider } from '..';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { VisTypesRegistryProvider } from '../../registry/vis_types';
|
||||
import { DataAdapter, RequestAdapter } from '../../inspector/adapters';
|
||||
import { Inspector } from '../../inspector/inspector';
|
||||
|
||||
describe('Vis Class', function () {
|
||||
let indexPattern;
|
||||
|
@ -118,145 +116,6 @@ describe('Vis Class', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('inspector', () => {
|
||||
|
||||
describe('hasInspector()', () => {
|
||||
it('should forward to inspectors hasInspector', () => {
|
||||
const vis = new Vis(indexPattern, state({
|
||||
inspectorAdapters: {
|
||||
data: true,
|
||||
requests: true,
|
||||
}
|
||||
}));
|
||||
sinon.spy(Inspector, 'isAvailable');
|
||||
vis.hasInspector();
|
||||
expect(Inspector.isAvailable.calledOnce).to.be(true);
|
||||
const adapters = Inspector.isAvailable.lastCall.args[0];
|
||||
expect(adapters.data).to.be.a(DataAdapter);
|
||||
expect(adapters.requests).to.be.a(RequestAdapter);
|
||||
});
|
||||
|
||||
it('should return hasInspectors result', () => {
|
||||
const vis = new Vis(indexPattern, state({}));
|
||||
const stub = sinon.stub(Inspector, 'isAvailable');
|
||||
stub.returns(true);
|
||||
expect(vis.hasInspector()).to.be(true);
|
||||
stub.returns(false);
|
||||
expect(vis.hasInspector()).to.be(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Inspector.isAvailable.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('openInspector()', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(Inspector, 'open');
|
||||
});
|
||||
|
||||
it('should call openInspector with all attached inspectors', () => {
|
||||
const Foodapter = class {};
|
||||
const vis = new Vis(indexPattern, state({
|
||||
inspectorAdapters: {
|
||||
data: true,
|
||||
custom: {
|
||||
foo: Foodapter
|
||||
}
|
||||
}
|
||||
}));
|
||||
vis.openInspector();
|
||||
expect(Inspector.open.calledOnce).to.be(true);
|
||||
const adapters = Inspector.open.lastCall.args[0];
|
||||
expect(adapters).to.be(vis.API.inspectorAdapters);
|
||||
});
|
||||
|
||||
it('should pass the vis title to the openInspector call', () => {
|
||||
const vis = new Vis(indexPattern, { ...state(), title: 'beautifulVis' });
|
||||
vis.openInspector();
|
||||
expect(Inspector.open.calledOnce).to.be(true);
|
||||
const params = Inspector.open.lastCall.args[1];
|
||||
expect(params.title).to.be('beautifulVis');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Inspector.open.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('inspectorAdapters', () => {
|
||||
|
||||
it('should register none for none requestHandler', () => {
|
||||
const vis = new Vis(indexPattern, state({ requestHandler: 'none' }));
|
||||
expect(vis.API.inspectorAdapters).to.eql({});
|
||||
});
|
||||
|
||||
it('should attach data and request handler for courier', () => {
|
||||
const vis = new Vis(indexPattern, state({ requestHandler: 'courier' }));
|
||||
expect(vis.API.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
expect(vis.API.inspectorAdapters.requests).to.be.a(RequestAdapter);
|
||||
});
|
||||
|
||||
it('should allow enabling data adapter manually', () => {
|
||||
const vis = new Vis(indexPattern, state({
|
||||
requestHandler: 'none',
|
||||
inspectorAdapters: {
|
||||
data: true,
|
||||
}
|
||||
}));
|
||||
expect(vis.API.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
});
|
||||
|
||||
it('should allow enabling requests adapter manually', () => {
|
||||
const vis = new Vis(indexPattern, state({
|
||||
requestHandler: 'none',
|
||||
inspectorAdapters: {
|
||||
requests: true,
|
||||
}
|
||||
}));
|
||||
expect(vis.API.inspectorAdapters.requests).to.be.a(RequestAdapter);
|
||||
});
|
||||
|
||||
it('should allow adding custom inspector adapters via the custom key', () => {
|
||||
const Foodapter = class {};
|
||||
const Bardapter = class {};
|
||||
const vis = new Vis(indexPattern, state({
|
||||
requestHandler: 'none',
|
||||
inspectorAdapters: {
|
||||
custom: {
|
||||
foo: Foodapter,
|
||||
bar: Bardapter,
|
||||
}
|
||||
}
|
||||
}));
|
||||
expect(vis.API.inspectorAdapters.foo).to.be.a(Foodapter);
|
||||
expect(vis.API.inspectorAdapters.bar).to.be.a(Bardapter);
|
||||
});
|
||||
|
||||
it('should not share adapter instances between vis instances', () => {
|
||||
const Foodapter = class {};
|
||||
const visState = state({
|
||||
inspectorAdapters: {
|
||||
data: true,
|
||||
custom: {
|
||||
foo: Foodapter
|
||||
}
|
||||
}
|
||||
});
|
||||
const vis1 = new Vis(indexPattern, visState);
|
||||
const vis2 = new Vis(indexPattern, visState);
|
||||
expect(vis1.API.inspectorAdapters.foo).to.be.a(Foodapter);
|
||||
expect(vis2.API.inspectorAdapters.foo).to.be.a(Foodapter);
|
||||
expect(vis1.API.inspectorAdapters.foo).not.to.be(vis2.API.inspectorAdapters.foo);
|
||||
expect(vis1.API.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
expect(vis2.API.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
expect(vis1.API.inspectorAdapters.data).not.to.be(vis2.API.inspectorAdapters.data);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('vis addFilter method', () => {
|
||||
let aggConfig;
|
||||
let data;
|
||||
|
|
|
@ -18,62 +18,28 @@
|
|||
*/
|
||||
|
||||
import { cloneDeep, has } from 'lodash';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { VisRequestHandlersRegistryProvider } from '../../registry/vis_request_handlers';
|
||||
import { calculateObjectHash } from '../lib/calculate_object_hash';
|
||||
import { getRequestInspectorStats, getResponseInspectorStats } from '../../courier/utils/courier_inspector_utils';
|
||||
import { tabifyAggResponse } from '../../agg_response/tabify/tabify';
|
||||
|
||||
import { FormattedData } from '../../inspector/adapters';
|
||||
import { buildTabularInspectorData } from '../../inspector/build_tabular_inspector_data';
|
||||
import { getTime } from '../../timefilter/get_time';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const CourierRequestHandlerProvider = function () {
|
||||
|
||||
/**
|
||||
* This function builds tabular data from the response and attaches it to the
|
||||
* inspector. It will only be called when the data view in the inspector is opened.
|
||||
*/
|
||||
async function buildTabularInspectorData(vis, searchSource, aggConfigs) {
|
||||
const table = tabifyAggResponse(aggConfigs, searchSource.finalResponse, {
|
||||
partialRows: true,
|
||||
metricsAtAllLevels: vis.isHierarchical(),
|
||||
});
|
||||
const columns = table.columns.map((col, index) => {
|
||||
const field = col.aggConfig.getField();
|
||||
const isCellContentFilterable =
|
||||
col.aggConfig.isFilterable()
|
||||
&& (!field || field.filterable);
|
||||
return ({
|
||||
name: col.name,
|
||||
field: `col${index}`,
|
||||
filter: isCellContentFilterable && ((value) => {
|
||||
const filter = col.aggConfig.createFilter(value.raw);
|
||||
vis.API.queryFilter.addFilters(filter);
|
||||
}),
|
||||
filterOut: isCellContentFilterable && ((value) => {
|
||||
const filter = col.aggConfig.createFilter(value.raw);
|
||||
filter.meta = filter.meta || {};
|
||||
filter.meta.negate = true;
|
||||
vis.API.queryFilter.addFilters(filter);
|
||||
}),
|
||||
});
|
||||
});
|
||||
const rows = table.rows.map(row => {
|
||||
return table.columns.reduce((prev, cur, index) => {
|
||||
const value = row[cur.id];
|
||||
const fieldFormatter = cur.aggConfig.fieldFormatter('text');
|
||||
prev[`col${index}`] = new FormattedData(value, fieldFormatter(value));
|
||||
return prev;
|
||||
}, {});
|
||||
});
|
||||
|
||||
return { columns, rows };
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'courier',
|
||||
handler: async function (vis, { searchSource, aggs, timeRange, query, filters, forceFetch, partialRows }) {
|
||||
handler: async function (vis, {
|
||||
searchSource,
|
||||
aggs,
|
||||
timeRange,
|
||||
query,
|
||||
filters,
|
||||
forceFetch,
|
||||
partialRows,
|
||||
inspectorAdapters,
|
||||
}) {
|
||||
|
||||
// Create a new search source that inherits the original search source
|
||||
// but has the appropriate timeRange applied via a filter.
|
||||
|
@ -123,14 +89,14 @@ const CourierRequestHandlerProvider = function () {
|
|||
const shouldQuery = forceFetch || (searchSource.lastQuery !== queryHash);
|
||||
|
||||
if (shouldQuery) {
|
||||
const lastAggConfig = aggs;
|
||||
vis.API.inspectorAdapters.requests.reset();
|
||||
const request = vis.API.inspectorAdapters.requests.start(
|
||||
inspectorAdapters.requests.reset();
|
||||
const request = inspectorAdapters.requests.start(
|
||||
i18n.translate('common.ui.vis.courier.inspector.dataRequest.title', { defaultMessage: 'Data' }),
|
||||
{
|
||||
description: i18n.translate('common.ui.vis.courier.inspector.dataRequest.description',
|
||||
{ defaultMessage: 'This request queries Elasticsearch to fetch the data for the visualization.' }),
|
||||
});
|
||||
}
|
||||
);
|
||||
request.stats(getRequestInspectorStats(requestSearchSource));
|
||||
|
||||
const response = await requestSearchSource.fetch();
|
||||
|
@ -151,18 +117,13 @@ const CourierRequestHandlerProvider = function () {
|
|||
aggs,
|
||||
agg,
|
||||
requestSearchSource,
|
||||
vis.API.inspectorAdapters
|
||||
inspectorAdapters
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
searchSource.finalResponse = resp;
|
||||
|
||||
vis.API.inspectorAdapters.data.setTabularLoader(
|
||||
() => buildTabularInspectorData(vis, searchSource, lastAggConfig),
|
||||
{ returnsFormattedValues: true }
|
||||
);
|
||||
|
||||
requestSearchSource.getSearchRequestBody().then(req => {
|
||||
request.json(req);
|
||||
});
|
||||
|
@ -185,6 +146,11 @@ const CourierRequestHandlerProvider = function () {
|
|||
searchSource.tabifiedResponse = tabifyAggResponse(tabifyAggs, searchSource.finalResponse, tabifyParams);
|
||||
}
|
||||
|
||||
inspectorAdapters.data.setTabularLoader(
|
||||
() => buildTabularInspectorData(searchSource.tabifiedResponse, vis.API.queryFilter),
|
||||
{ returnsFormattedValues: true }
|
||||
);
|
||||
|
||||
return searchSource.tabifiedResponse;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
import { SearchSource } from '../../courier';
|
||||
import { QueryFilter } from '../../filter_bar/query_filter';
|
||||
import { Adapters } from '../../inspector/types';
|
||||
import { PersistedState } from '../../persisted_state';
|
||||
import { Filters, Query, TimeRange } from '../../visualize';
|
||||
import { AggConfigs } from '../agg_configs';
|
||||
|
@ -34,6 +35,7 @@ export interface RequestHandlerParams {
|
|||
queryFilter: QueryFilter;
|
||||
uiState: PersistedState;
|
||||
partialRows?: boolean;
|
||||
inspectorAdapters?: Adapters;
|
||||
}
|
||||
|
||||
export type RequestHandler = <T>(vis: Vis, params: RequestHandlerParams) => T;
|
||||
|
|
|
@ -40,9 +40,6 @@ import { SearchSourceProvider } from '../courier/search_source';
|
|||
import { SavedObjectsClientProvider } from '../saved_objects';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
|
||||
import { Inspector } from '../inspector';
|
||||
import { RequestAdapter, DataAdapter } from '../inspector/adapters';
|
||||
|
||||
const getTerms = (table, columnIndex, rowIndex) => {
|
||||
if (rowIndex === -1) {
|
||||
return [];
|
||||
|
@ -132,59 +129,10 @@ export function VisProvider(Private, indexPatterns, getAppState) {
|
|||
onBrushEvent(event, getAppState());
|
||||
}
|
||||
},
|
||||
inspectorAdapters: this._getActiveInspectorAdapters(),
|
||||
getAppState,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the inspector for this visualization.
|
||||
* @return {InspectorSession} the handler for the session of this inspector.
|
||||
*/
|
||||
openInspector() {
|
||||
return Inspector.open(this.API.inspectorAdapters, {
|
||||
title: this.title
|
||||
});
|
||||
}
|
||||
|
||||
hasInspector() {
|
||||
return Inspector.isAvailable(this.API.inspectorAdapters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object of all inspectors for this vis object.
|
||||
* This must only be called after this.type has properly be initialized,
|
||||
* since we need to read out data from the the vis type to check which
|
||||
* inspectors are available.
|
||||
*/
|
||||
_getActiveInspectorAdapters() {
|
||||
const adapters = {};
|
||||
const { inspectorAdapters: typeAdapters } = this.type;
|
||||
|
||||
// Add the requests inspector adapters if the vis type explicitly requested it via
|
||||
// inspectorAdapters.requests: true in its definition or if it's using the courier
|
||||
// request handler, since that will automatically log its requests.
|
||||
if (typeAdapters && typeAdapters.requests || this.type.requestHandler === 'courier') {
|
||||
adapters.requests = new RequestAdapter();
|
||||
}
|
||||
|
||||
// Add the data inspector adapter if the vis type requested it or if the
|
||||
// vis is using courier, since we know that courier supports logging
|
||||
// its data.
|
||||
if (typeAdapters && typeAdapters.data || this.type.requestHandler === 'courier') {
|
||||
adapters.data = new DataAdapter();
|
||||
}
|
||||
|
||||
// Add all inspectors, that are explicitly registered with this vis type
|
||||
if (typeAdapters && typeAdapters.custom) {
|
||||
Object.entries(typeAdapters.custom).forEach(([key, Adapter]) => {
|
||||
adapters[key] = new Adapter();
|
||||
});
|
||||
}
|
||||
|
||||
return adapters;
|
||||
}
|
||||
|
||||
setCurrentState(state) {
|
||||
this.title = state.title || '';
|
||||
const type = state.type || this.type;
|
||||
|
|
|
@ -34,6 +34,8 @@ import { Inspector } from '../../../inspector/inspector';
|
|||
import { dispatchRenderComplete } from '../../../render_complete';
|
||||
import { VisualizeDataLoader } from '../visualize_data_loader';
|
||||
import { PersistedState } from '../../../persisted_state';
|
||||
import { DataAdapter } from '../../../inspector/adapters/data';
|
||||
import { RequestAdapter } from '../../../inspector/adapters/request';
|
||||
|
||||
describe('visualize loader', () => {
|
||||
|
||||
|
@ -79,6 +81,7 @@ describe('visualize loader', () => {
|
|||
const Vis = Private(VisProvider);
|
||||
vis = new Vis(indexPattern, {
|
||||
type: 'pie',
|
||||
title: 'testVis',
|
||||
params: {},
|
||||
aggs: [
|
||||
{ type: 'count', schema: 'metric' },
|
||||
|
@ -95,7 +98,7 @@ describe('visualize loader', () => {
|
|||
}
|
||||
]
|
||||
});
|
||||
vis.type.requestHandler = 'none';
|
||||
vis.type.requestHandler = 'courier';
|
||||
vis.type.responseHandler = 'none';
|
||||
vis.type.requiresSearch = false;
|
||||
|
||||
|
@ -239,6 +242,116 @@ describe('visualize loader', () => {
|
|||
inspectorSession.close();
|
||||
});
|
||||
|
||||
describe('inspector', () => {
|
||||
|
||||
describe('hasInspector()', () => {
|
||||
it('should forward to inspectors hasInspector', () => {
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], createSavedObject(), {});
|
||||
sinon.spy(Inspector, 'isAvailable');
|
||||
handler.hasInspector();
|
||||
expect(Inspector.isAvailable.calledOnce).to.be(true);
|
||||
const adapters = Inspector.isAvailable.lastCall.args[0];
|
||||
expect(adapters.data).to.be.a(DataAdapter);
|
||||
expect(adapters.requests).to.be.a(RequestAdapter);
|
||||
});
|
||||
|
||||
it('should return hasInspectors result', () => {
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], createSavedObject(), {});
|
||||
const stub = sinon.stub(Inspector, 'isAvailable');
|
||||
stub.returns(true);
|
||||
expect(handler.hasInspector()).to.be(true);
|
||||
stub.returns(false);
|
||||
expect(handler.hasInspector()).to.be(false);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Inspector.isAvailable.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('openInspector()', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(Inspector, 'open');
|
||||
});
|
||||
|
||||
it('should call openInspector with all attached inspectors', () => {
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], createSavedObject(), {});
|
||||
handler.openInspector();
|
||||
expect(Inspector.open.calledOnce).to.be(true);
|
||||
const adapters = Inspector.open.lastCall.args[0];
|
||||
expect(adapters).to.be(handler.inspectorAdapters);
|
||||
});
|
||||
|
||||
it('should pass the vis title to the openInspector call', () => {
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], createSavedObject(), {});
|
||||
handler.openInspector();
|
||||
expect(Inspector.open.calledOnce).to.be(true);
|
||||
const params = Inspector.open.lastCall.args[1];
|
||||
expect(params.title).to.be('testVis');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
Inspector.open.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('inspectorAdapters', () => {
|
||||
|
||||
it('should register none for none requestHandler', () => {
|
||||
const savedObj = createSavedObject();
|
||||
savedObj.vis.type.requestHandler = 'none';
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], savedObj, {});
|
||||
expect(handler.inspectorAdapters).to.eql({});
|
||||
});
|
||||
|
||||
it('should attach data and request handler for courier', () => {
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], createSavedObject(), {});
|
||||
expect(handler.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
expect(handler.inspectorAdapters.requests).to.be.a(RequestAdapter);
|
||||
});
|
||||
|
||||
it('should allow enabling data adapter manually', () => {
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], createSavedObject(), {});
|
||||
expect(handler.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
});
|
||||
|
||||
it('should allow enabling requests adapter manually', () => {
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], createSavedObject(), {});
|
||||
expect(handler.inspectorAdapters.requests).to.be.a(RequestAdapter);
|
||||
});
|
||||
|
||||
it('should allow adding custom inspector adapters via the custom key', () => {
|
||||
const Foodapter = class {};
|
||||
const Bardapter = class {};
|
||||
const savedObj = createSavedObject();
|
||||
savedObj.vis.type.inspectorAdapters = {
|
||||
custom: { foo: Foodapter, bar: Bardapter }
|
||||
};
|
||||
const handler = loader.embedVisualizationWithSavedObject(newContainer()[0], savedObj, {});
|
||||
expect(handler.inspectorAdapters.foo).to.be.a(Foodapter);
|
||||
expect(handler.inspectorAdapters.bar).to.be.a(Bardapter);
|
||||
});
|
||||
|
||||
it('should not share adapter instances between vis instances', () => {
|
||||
const Foodapter = class {};
|
||||
const savedObj1 = createSavedObject();
|
||||
const savedObj2 = createSavedObject();
|
||||
savedObj1.vis.type.inspectorAdapters = { custom: { foo: Foodapter } };
|
||||
savedObj2.vis.type.inspectorAdapters = { custom: { foo: Foodapter } };
|
||||
const handler1 = loader.embedVisualizationWithSavedObject(newContainer()[0], savedObj1, {});
|
||||
const handler2 = loader.embedVisualizationWithSavedObject(newContainer()[0], savedObj2, {});
|
||||
expect(handler1.inspectorAdapters.foo).to.be.a(Foodapter);
|
||||
expect(handler2.inspectorAdapters.foo).to.be.a(Foodapter);
|
||||
expect(handler1.inspectorAdapters.foo).not.to.be(handler2.inspectorAdapters.foo);
|
||||
expect(handler1.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
expect(handler2.inspectorAdapters.data).to.be.a(DataAdapter);
|
||||
expect(handler1.inspectorAdapters.data).not.to.be(handler2.inspectorAdapters.data);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should have whenFirstRenderComplete returns a promise resolving on first renderComplete event', async () => {
|
||||
const container = newContainer();
|
||||
const handler = loader.embedVisualizationWithSavedObject(container[0], createSavedObject(), {});
|
||||
|
|
|
@ -19,8 +19,7 @@
|
|||
|
||||
import { EventEmitter } from 'events';
|
||||
import { debounce } from 'lodash';
|
||||
import { Inspector } from '../../inspector';
|
||||
|
||||
import { Adapters } from '../../inspector/types';
|
||||
import { PersistedState } from '../../persisted_state';
|
||||
import { IPrivate } from '../../private';
|
||||
import { RenderCompleteHelper } from '../../render_complete';
|
||||
|
@ -30,6 +29,9 @@ import { RequestHandlerParams, Vis } from '../../vis';
|
|||
import { visualizationLoader } from './visualization_loader';
|
||||
import { VisualizeDataLoader } from './visualize_data_loader';
|
||||
|
||||
import { Inspector } from '../../inspector';
|
||||
import { DataAdapter, RequestAdapter } from '../../inspector/adapters';
|
||||
|
||||
import { VisSavedObject, VisualizeLoaderParams, VisualizeUpdateParams } from './types';
|
||||
|
||||
interface EmbeddedVisualizeHandlerParams extends VisualizeLoaderParams {
|
||||
|
@ -67,6 +69,7 @@ export class EmbeddedVisualizeHandler {
|
|||
private readonly appState?: AppState;
|
||||
private uiState: PersistedState;
|
||||
private dataLoader: VisualizeDataLoader;
|
||||
private inspectorAdapters: Adapters = {};
|
||||
|
||||
constructor(
|
||||
private readonly element: HTMLElement,
|
||||
|
@ -110,6 +113,9 @@ export class EmbeddedVisualizeHandler {
|
|||
|
||||
this.dataLoader = new VisualizeDataLoader(vis, Private);
|
||||
this.renderCompleteHelper = new RenderCompleteHelper(element);
|
||||
this.inspectorAdapters = this.getActiveInspectorAdapters();
|
||||
this.vis.openInspector = this.openInspector;
|
||||
this.vis.hasInspector = this.hasInspector;
|
||||
|
||||
this.render();
|
||||
}
|
||||
|
@ -184,9 +190,15 @@ export class EmbeddedVisualizeHandler {
|
|||
* handler to the inspector to close and interact with it.
|
||||
* @return An inspector session to interact with the opened inspector.
|
||||
*/
|
||||
public openInspector(): ReturnType<typeof Inspector.open> {
|
||||
return this.vis.openInspector();
|
||||
}
|
||||
public openInspector = () => {
|
||||
return Inspector.open(this.inspectorAdapters, {
|
||||
title: this.vis.title,
|
||||
});
|
||||
};
|
||||
|
||||
public hasInspector = () => {
|
||||
return Inspector.isAvailable(this.inspectorAdapters);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a promise, that will resolve (without a value) once the first rendering of
|
||||
|
@ -230,6 +242,40 @@ export class EmbeddedVisualizeHandler {
|
|||
this.fetchAndRender();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object of all inspectors for this vis object.
|
||||
* This must only be called after this.type has properly be initialized,
|
||||
* since we need to read out data from the the vis type to check which
|
||||
* inspectors are available.
|
||||
*/
|
||||
private getActiveInspectorAdapters = (): Adapters => {
|
||||
const adapters: Adapters = {};
|
||||
const { inspectorAdapters: typeAdapters } = this.vis.type;
|
||||
|
||||
// Add the requests inspector adapters if the vis type explicitly requested it via
|
||||
// inspectorAdapters.requests: true in its definition or if it's using the courier
|
||||
// request handler, since that will automatically log its requests.
|
||||
if ((typeAdapters && typeAdapters.requests) || this.vis.type.requestHandler === 'courier') {
|
||||
adapters.requests = new RequestAdapter();
|
||||
}
|
||||
|
||||
// Add the data inspector adapter if the vis type requested it or if the
|
||||
// vis is using courier, since we know that courier supports logging
|
||||
// its data.
|
||||
if ((typeAdapters && typeAdapters.data) || this.vis.type.requestHandler === 'courier') {
|
||||
adapters.data = new DataAdapter();
|
||||
}
|
||||
|
||||
// Add all inspectors, that are explicitly registered with this vis type
|
||||
if (typeAdapters && typeAdapters.custom) {
|
||||
Object.entries(typeAdapters.custom).forEach(([key, Adapter]) => {
|
||||
adapters[key] = new (Adapter as any)();
|
||||
});
|
||||
}
|
||||
|
||||
return adapters;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches new data and renders the chart. This will happen debounced for a couple
|
||||
* of milliseconds, to bundle fast successive calls into one fetch and render,
|
||||
|
@ -265,6 +311,7 @@ export class EmbeddedVisualizeHandler {
|
|||
private fetch = (forceFetch: boolean = false) => {
|
||||
this.dataLoaderParams.aggs = this.vis.getAggConfig();
|
||||
this.dataLoaderParams.forceFetch = forceFetch;
|
||||
this.dataLoaderParams.inspectorAdapters = this.inspectorAdapters;
|
||||
|
||||
return this.dataLoader.fetch(this.dataLoaderParams);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue