Skip to content

Commit

Permalink
remove inspector from Vis (elastic#24112)
Browse files Browse the repository at this point in the history
  • Loading branch information
ppisljar committed Nov 6, 2018
1 parent 6a36a77 commit b922cb9
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 258 deletions.
4 changes: 2 additions & 2 deletions src/core_plugins/kibana/public/visualize/editor/editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.';
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
57 changes: 57 additions & 0 deletions src/ui/public/inspector/build_tabular_inspector_data.js
Original file line number Diff line number Diff line change
@@ -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 };
}
141 changes: 0 additions & 141 deletions src/ui/public/vis/__tests__/_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
78 changes: 22 additions & 56 deletions src/ui/public/vis/request_handlers/courier.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand All @@ -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);
});
Expand All @@ -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;
}
};
Expand Down
2 changes: 2 additions & 0 deletions src/ui/public/vis/request_handlers/request_handlers.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -34,6 +35,7 @@ export interface RequestHandlerParams {
queryFilter: QueryFilter;
uiState: PersistedState;
partialRows?: boolean;
inspectorAdapters?: Adapters;
}

export type RequestHandler = <T>(vis: Vis, params: RequestHandlerParams) => T;
Expand Down
Loading

0 comments on commit b922cb9

Please sign in to comment.