Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove inspector from Vis #24112

Merged
merged 3 commits into from
Nov 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be removed in the future, as all the visualize elements will have inspector when we start using pipeline

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe can you already open an issue for that? so we are aware to change this later?

},
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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved out of courier .... with the new table implementation we will get rid of this.

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