mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
parent
3096c36117
commit
87260d266f
24 changed files with 681 additions and 755 deletions
|
@ -1,10 +1,9 @@
|
|||
// import 'ui/agg_table';
|
||||
import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify/tabify';
|
||||
import tableSpyModeTemplate from 'plugins/spy_modes/table_spy_mode.html';
|
||||
import { SpyModesRegistryProvider } from 'ui/registry/spy_modes';
|
||||
|
||||
function VisSpyTableProvider(Notifier, $filter, $rootScope, config, Private) {
|
||||
const tabifyAggResponse = Private(AggResponseTabifyProvider);
|
||||
function VisSpyTableProvider(Notifier, $filter, $rootScope) {
|
||||
const PER_PAGE_DEFAULT = 10;
|
||||
|
||||
return {
|
||||
|
@ -23,10 +22,11 @@ function VisSpyTableProvider(Notifier, $filter, $rootScope, config, Private) {
|
|||
} else {
|
||||
$scope.rowsPerPage = PER_PAGE_DEFAULT;
|
||||
|
||||
$scope.table = tabifyAggResponse($scope.vis, $scope.searchSource.rawResponse, {
|
||||
$scope.table = tabifyAggResponse($scope.vis.getAggConfig().getResponseAggs(), $scope.searchSource.rawResponse, {
|
||||
canSplit: false,
|
||||
asAggConfigResults: true,
|
||||
partialRows: true
|
||||
partialRows: true,
|
||||
isHierarchical: $scope.vis.isHierarchical()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -2,8 +2,7 @@ import $ from 'jquery';
|
|||
import _ from 'lodash';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import sinon from 'sinon';
|
||||
import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify/tabify';
|
||||
import { VisProvider } from 'ui/vis';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { AppStateProvider } from 'ui/state_management/app_state';
|
||||
|
@ -17,12 +16,10 @@ describe('Controller', function () {
|
|||
let Vis;
|
||||
let fixtures;
|
||||
let AppState;
|
||||
let tabify;
|
||||
|
||||
beforeEach(ngMock.module('kibana', 'kibana/table_vis'));
|
||||
beforeEach(ngMock.inject(function ($injector) {
|
||||
Private = $injector.get('Private');
|
||||
tabify = Private(AggResponseTabifyProvider);
|
||||
$rootScope = $injector.get('$rootScope');
|
||||
$compile = $injector.get('$compile');
|
||||
fixtures = require('fixtures/fake_hierarchical_data');
|
||||
|
@ -90,7 +87,9 @@ describe('Controller', function () {
|
|||
expect(!$scope.tableGroups).to.be.ok();
|
||||
expect(!$scope.hasSomeRows).to.be.ok();
|
||||
|
||||
attachEsResponseToScope(tabify(vis, fixtures.oneRangeBucket));
|
||||
attachEsResponseToScope(tabifyAggResponse(vis.getAggConfig().getResponseAggs(), fixtures.oneRangeBucket, {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
}));
|
||||
|
||||
expect($scope.hasSomeRows).to.be(true);
|
||||
expect($scope.tableGroups).to.have.property('tables');
|
||||
|
@ -103,7 +102,9 @@ describe('Controller', function () {
|
|||
const vis = new OneRangeVis();
|
||||
initController(vis);
|
||||
|
||||
attachEsResponseToScope(tabify(vis, fixtures.oneRangeBucket));
|
||||
attachEsResponseToScope(tabifyAggResponse(vis.getAggConfig().getResponseAggs(), fixtures.oneRangeBucket, {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
}));
|
||||
removeEsResponseFromScope();
|
||||
|
||||
expect(!$scope.hasSomeRows).to.be.ok();
|
||||
|
@ -122,7 +123,9 @@ describe('Controller', function () {
|
|||
const resp = _.cloneDeep(fixtures.oneRangeBucket);
|
||||
resp.aggregations.agg_2.buckets = {};
|
||||
|
||||
attachEsResponseToScope(tabify(vis, resp));
|
||||
attachEsResponseToScope(tabifyAggResponse(vis.getAggConfig().getResponseAggs(), resp, {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
}));
|
||||
|
||||
expect($scope.sort.columnIndex).to.equal(sortObj.columnIndex);
|
||||
expect($scope.sort.direction).to.equal(sortObj.direction);
|
||||
|
@ -136,35 +139,27 @@ describe('Controller', function () {
|
|||
const resp = _.cloneDeep(fixtures.oneRangeBucket);
|
||||
resp.aggregations.agg_2.buckets = {};
|
||||
|
||||
attachEsResponseToScope(tabify(vis, resp));
|
||||
attachEsResponseToScope(tabifyAggResponse(vis.getAggConfig().getResponseAggs(), resp, {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
}));
|
||||
|
||||
expect($scope.hasSomeRows).to.be(false);
|
||||
expect(!$scope.tableGroups).to.be.ok();
|
||||
});
|
||||
|
||||
it('passes partialRows:true to tabify based on the vis params', function () {
|
||||
// spy on the tabify private module
|
||||
const spiedTabify = sinon.spy(Private(AggResponseTabifyProvider));
|
||||
Private.stub(AggResponseTabifyProvider, spiedTabify);
|
||||
|
||||
const vis = new OneRangeVis({ showPartialRows: true });
|
||||
initController(vis);
|
||||
attachEsResponseToScope(spiedTabify(vis, fixtures.oneRangeBucket));
|
||||
|
||||
expect(spiedTabify).to.have.property('callCount', 1);
|
||||
expect(spiedTabify.firstCall.args[0].isHierarchical()).to.equal(true);
|
||||
expect(vis.isHierarchical()).to.equal(true);
|
||||
});
|
||||
|
||||
it('passes partialRows:false to tabify based on the vis params', function () {
|
||||
// spy on the tabify private module
|
||||
const spiedTabify = sinon.spy(Private(AggResponseTabifyProvider));
|
||||
Private.stub(AggResponseTabifyProvider, spiedTabify);
|
||||
|
||||
const vis = new OneRangeVis({ showPartialRows: false });
|
||||
initController(vis);
|
||||
attachEsResponseToScope(spiedTabify(vis, fixtures.oneRangeBucket));
|
||||
|
||||
expect(spiedTabify).to.have.property('callCount', 1);
|
||||
expect(spiedTabify.firstCall.args[0].isHierarchical()).to.equal(false);
|
||||
expect(vis.isHierarchical()).to.equal(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,12 +5,11 @@ import ngMock from 'ng_mock';
|
|||
import { VisProvider } from 'ui/vis';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import FixturesAggRespGeohashGridProvider from 'fixtures/agg_resp/geohash_grid';
|
||||
import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify/tabify';
|
||||
import { AggResponseGeoJsonProvider } from 'ui/agg_response/geo_json/geo_json';
|
||||
|
||||
describe('GeoJson Agg Response Converter', function () {
|
||||
let vis;
|
||||
let tabify;
|
||||
let convert;
|
||||
let esResponse;
|
||||
let expectedAggs;
|
||||
|
@ -23,7 +22,6 @@ describe('GeoJson Agg Response Converter', function () {
|
|||
const indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
|
||||
esResponse = Private(FixturesAggRespGeohashGridProvider);
|
||||
tabify = Private(AggResponseTabifyProvider);
|
||||
convert = Private(AggResponseGeoJsonProvider);
|
||||
|
||||
createVis = function (useGeocentroid) {
|
||||
|
@ -54,7 +52,7 @@ describe('GeoJson Agg Response Converter', function () {
|
|||
[ { asAggConfigResults: true }, { asAggConfigResults: false } ].forEach(function (tableOpts) {
|
||||
|
||||
function makeTable() {
|
||||
return _.sample(tabify(vis, esResponse, tableOpts).tables);
|
||||
return _.sample(tabifyAggResponse(vis.getAggConfig().getResponseAggs(), esResponse, tableOpts).tables);
|
||||
}
|
||||
|
||||
function makeSingleChart(table) {
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { BuildHierarchicalDataProvider } from 'ui/agg_response/hierarchical/build_hierarchical_data';
|
||||
import { AggResponsePointSeriesProvider } from 'ui/agg_response/point_series/point_series';
|
||||
import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify/tabify';
|
||||
import { AggResponseGeoJsonProvider } from 'ui/agg_response/geo_json/geo_json';
|
||||
|
||||
export function AggResponseIndexProvider(Private) {
|
||||
return {
|
||||
hierarchical: Private(BuildHierarchicalDataProvider),
|
||||
pointSeries: Private(AggResponsePointSeriesProvider),
|
||||
tabify: Private(AggResponseTabifyProvider),
|
||||
tabify: tabifyAggResponse,
|
||||
geoJson: Private(AggResponseGeoJsonProvider)
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import AggConfigResult from 'ui/vis/agg_config_result';
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { VisProvider } from 'ui/vis';
|
||||
import { AggResponseTabifyTableProvider } from 'ui/agg_response/tabify/_table';
|
||||
import { TabifyTable } from 'ui/agg_response/tabify/_table';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { AggResponsePointSeriesProvider } from 'ui/agg_response/point_series/point_series';
|
||||
|
||||
|
@ -14,13 +14,11 @@ describe('pointSeriesChartDataFromTable', function () {
|
|||
|
||||
let pointSeriesChartDataFromTable;
|
||||
let indexPattern;
|
||||
let Table;
|
||||
let Vis;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
Vis = Private(VisProvider);
|
||||
Table = Private(AggResponseTabifyTableProvider);
|
||||
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
pointSeriesChartDataFromTable = Private(AggResponsePointSeriesProvider);
|
||||
}));
|
||||
|
@ -30,7 +28,7 @@ describe('pointSeriesChartDataFromTable', function () {
|
|||
const agg = vis.aggs[0];
|
||||
const result = new AggConfigResult(vis.aggs[0], void 0, 100, 100);
|
||||
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
table.columns = [ { aggConfig: agg } ];
|
||||
table.rows.push([ result ]);
|
||||
|
||||
|
@ -69,7 +67,7 @@ describe('pointSeriesChartDataFromTable', function () {
|
|||
};
|
||||
|
||||
const rowCount = 3;
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
table.columns = [ x.col, y.col ];
|
||||
_.times(rowCount, function (i) {
|
||||
const date = new AggConfigResult(x.agg, void 0, x.at(i));
|
||||
|
@ -130,7 +128,7 @@ describe('pointSeriesChartDataFromTable', function () {
|
|||
};
|
||||
|
||||
const rowCount = 3;
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
table.columns = [ date.col, avg.col, max.col ];
|
||||
_.times(rowCount, function (i) {
|
||||
const dateResult = new AggConfigResult(date.agg, void 0, date.at(i));
|
||||
|
@ -209,7 +207,7 @@ describe('pointSeriesChartDataFromTable', function () {
|
|||
const metricCount = 2;
|
||||
const rowsPerSegment = 2;
|
||||
const rowCount = extensions.length * rowsPerSegment;
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
table.columns = [ date.col, term.col, avg.col, max.col ];
|
||||
_.times(rowCount, function (i) {
|
||||
const dateResult = new AggConfigResult(date.agg, void 0, date.at(i));
|
||||
|
|
|
@ -1,24 +1,16 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { AggResponseBucketsProvider } from 'ui/agg_response/tabify/_buckets';
|
||||
import { TabifyBuckets } from 'ui/agg_response/tabify/_buckets';
|
||||
|
||||
describe('Buckets wrapper', function () {
|
||||
let Buckets;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
Buckets = Private(AggResponseBucketsProvider);
|
||||
}));
|
||||
|
||||
|
||||
function test(aggResp, count, keys) {
|
||||
it('reads the length', function () {
|
||||
const buckets = new Buckets(aggResp);
|
||||
const buckets = new TabifyBuckets(aggResp);
|
||||
expect(buckets).to.have.length(count);
|
||||
});
|
||||
|
||||
it('itterates properly, passing in the key', function () {
|
||||
const buckets = new Buckets(aggResp);
|
||||
const buckets = new TabifyBuckets(aggResp);
|
||||
const keysSent = [];
|
||||
buckets.forEach(function (bucket, key) {
|
||||
keysSent.push(key);
|
||||
|
@ -65,7 +57,7 @@ describe('Buckets wrapper', function () {
|
|||
single_bucket: {},
|
||||
doc_count: 5
|
||||
};
|
||||
const buckets = new Buckets(aggResp);
|
||||
const buckets = new TabifyBuckets(aggResp);
|
||||
expect(buckets).to.have.length(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { AggResponseGetColumnsProvider } from 'ui/agg_response/tabify/_get_columns';
|
||||
import { tabifyGetColumns } from 'ui/agg_response/tabify/_get_columns';
|
||||
import { VisProvider } from 'ui/vis';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
describe('get columns', function () {
|
||||
let getColumns;
|
||||
let Vis;
|
||||
let indexPattern;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
getColumns = Private(AggResponseGetColumnsProvider);
|
||||
Vis = Private(VisProvider);
|
||||
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
}));
|
||||
|
@ -20,7 +18,7 @@ describe('get columns', function () {
|
|||
type: 'pie'
|
||||
});
|
||||
while (vis.aggs.length) vis.aggs.pop();
|
||||
const columns = getColumns(vis);
|
||||
const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), null, vis.isHierarchical());
|
||||
|
||||
expect(columns).to.have.length(1);
|
||||
expect(columns[0]).to.have.property('aggConfig');
|
||||
|
@ -35,7 +33,7 @@ describe('get columns', function () {
|
|||
]
|
||||
});
|
||||
|
||||
const columns = getColumns(vis);
|
||||
const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), null, vis.isHierarchical());
|
||||
|
||||
expect(columns).to.have.length(2);
|
||||
expect(columns[1]).to.have.property('aggConfig');
|
||||
|
@ -53,7 +51,7 @@ describe('get columns', function () {
|
|||
]
|
||||
});
|
||||
|
||||
const columns = getColumns(vis);
|
||||
const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), null, vis.isHierarchical());
|
||||
|
||||
expect(columns).to.have.length(8);
|
||||
columns.forEach(function (column, i) {
|
||||
|
@ -75,7 +73,7 @@ describe('get columns', function () {
|
|||
]
|
||||
});
|
||||
|
||||
const columns = getColumns(vis);
|
||||
const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), null, vis.isHierarchical());
|
||||
|
||||
function checkColumns(column, i) {
|
||||
expect(column).to.have.property('aggConfig');
|
||||
|
@ -111,7 +109,7 @@ describe('get columns', function () {
|
|||
]
|
||||
});
|
||||
|
||||
const columns = getColumns(vis);
|
||||
const columns = tabifyGetColumns(vis.getAggConfig().getResponseAggs(), null, vis.isHierarchical());
|
||||
expect(columns).to.have.length(6);
|
||||
|
||||
// sum should be last
|
||||
|
|
|
@ -2,18 +2,16 @@ import _ from 'lodash';
|
|||
import fixtures from 'fixtures/fake_hierarchical_data';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify/tabify';
|
||||
import { VisProvider } from 'ui/vis';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
|
||||
describe('tabifyAggResponse Integration', function () {
|
||||
let Vis;
|
||||
let indexPattern;
|
||||
let tabifyAggResponse;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
tabifyAggResponse = Private(AggResponseTabifyProvider);
|
||||
Vis = Private(VisProvider);
|
||||
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
}));
|
||||
|
@ -31,7 +29,10 @@ describe('tabifyAggResponse Integration', function () {
|
|||
});
|
||||
normalizeIds(vis);
|
||||
|
||||
const resp = tabifyAggResponse(vis, fixtures.metricOnly, { canSplit: false });
|
||||
const resp = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), fixtures.metricOnly, {
|
||||
canSplit: false,
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
expect(resp).to.not.have.property('tables');
|
||||
expect(resp).to.have.property('rows').and.property('columns');
|
||||
|
@ -154,7 +155,7 @@ describe('tabifyAggResponse Integration', function () {
|
|||
// only complete rows, and only put the metrics at the end.
|
||||
|
||||
vis.isHierarchical = _.constant(false);
|
||||
const tabbed = tabifyAggResponse(vis, esResp);
|
||||
const tabbed = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), esResp, { isHierarchical: vis.isHierarchical() });
|
||||
|
||||
expectRootGroup(tabbed, function expectTable(table, splitKey) {
|
||||
expectColumns(table, [src, os, avg]);
|
||||
|
@ -180,8 +181,9 @@ describe('tabifyAggResponse Integration', function () {
|
|||
// the existing bucket and it's metric
|
||||
|
||||
vis.isHierarchical = _.constant(true);
|
||||
const tabbed = tabifyAggResponse(vis, esResp, {
|
||||
partialRows: true
|
||||
const tabbed = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), esResp, {
|
||||
partialRows: true,
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
expectRootGroup(tabbed, function expectTable(table, splitKey) {
|
||||
|
@ -214,9 +216,10 @@ describe('tabifyAggResponse Integration', function () {
|
|||
// the end
|
||||
|
||||
vis.isHierarchical = _.constant(true);
|
||||
const tabbed = tabifyAggResponse(vis, esResp, {
|
||||
const tabbed = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), esResp, {
|
||||
partialRows: true,
|
||||
minimalColumns: true
|
||||
minimalColumns: true,
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
expectRootGroup(tabbed, function expectTable(table, splitKey) {
|
||||
|
@ -246,8 +249,9 @@ describe('tabifyAggResponse Integration', function () {
|
|||
// create metric columns after each bucket
|
||||
|
||||
vis.isHierarchical = _.constant(false);
|
||||
const tabbed = tabifyAggResponse(vis, esResp, {
|
||||
minimalColumns: false
|
||||
const tabbed = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), esResp, {
|
||||
minimalColumns: false,
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
expectRootGroup(tabbed, function expectTable(table) {
|
||||
|
|
|
@ -2,84 +2,44 @@ import _ from 'lodash';
|
|||
import sinon from 'sinon';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { TabbedAggResponseWriterProvider } from 'ui/agg_response/tabify/_response_writer';
|
||||
import { AggResponseTabifyTableGroupProvider } from 'ui/agg_response/tabify/_table_group';
|
||||
import { AggResponseBucketsProvider } from 'ui/agg_response/tabify/_buckets';
|
||||
import { AggResponseGetColumnsProvider } from 'ui/agg_response/tabify/_get_columns';
|
||||
import { TabbedAggResponseWriter } from 'ui/agg_response/tabify/_response_writer';
|
||||
import { TabifyTableGroup } from 'ui/agg_response/tabify/_table_group';
|
||||
import { TabifyBuckets } from 'ui/agg_response/tabify/_buckets';
|
||||
import { VisProvider } from 'ui/vis';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
|
||||
describe('ResponseWriter class', function () {
|
||||
describe('TabbedAggResponseWriter class', function () {
|
||||
let Vis;
|
||||
let Buckets;
|
||||
let Private;
|
||||
let TableGroup;
|
||||
let getColumns;
|
||||
let indexPattern;
|
||||
let ResponseWriter;
|
||||
|
||||
function defineSetup(stubGetColumns) {
|
||||
function defineSetup() {
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function ($injector) {
|
||||
Private = $injector.get('Private');
|
||||
|
||||
if (stubGetColumns) {
|
||||
getColumns = sinon.stub();
|
||||
Private.stub(AggResponseGetColumnsProvider, getColumns);
|
||||
}
|
||||
|
||||
ResponseWriter = Private(TabbedAggResponseWriterProvider);
|
||||
TableGroup = Private(AggResponseTabifyTableGroupProvider);
|
||||
Buckets = Private(AggResponseBucketsProvider);
|
||||
Vis = Private(VisProvider);
|
||||
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
}));
|
||||
}
|
||||
|
||||
describe('Constructor', function () {
|
||||
defineSetup(true);
|
||||
|
||||
it('gets the columns for the vis', function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
new ResponseWriter(vis);
|
||||
|
||||
expect(getColumns).to.have.property('callCount', 1);
|
||||
expect(getColumns.firstCall.args[0]).to.be(vis);
|
||||
});
|
||||
|
||||
it('collects the aggConfigs from each column in aggStack', function () {
|
||||
const aggs = [
|
||||
{ type: 'date_histogram', schema: 'segment', params: { field: '@timestamp' } },
|
||||
{ type: 'terms', schema: 'segment', params: { field: 'extension' } },
|
||||
{ type: 'avg', schema: 'metric', params: { field: 'bytes' } }
|
||||
];
|
||||
|
||||
getColumns.returns(aggs.map(function (agg) {
|
||||
return { aggConfig: agg };
|
||||
}));
|
||||
|
||||
const vis = new Vis(indexPattern, {
|
||||
type: 'histogram',
|
||||
aggs: aggs
|
||||
});
|
||||
|
||||
const writer = new ResponseWriter(vis);
|
||||
expect(writer.aggStack).to.be.an('array');
|
||||
expect(writer.aggStack).to.have.length(aggs.length);
|
||||
writer.aggStack.forEach(function (agg, i) {
|
||||
expect(agg).to.be(aggs[i]);
|
||||
});
|
||||
});
|
||||
defineSetup();
|
||||
|
||||
it('sets canSplit=true by default', function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
const writer = new ResponseWriter(vis);
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
expect(writer).to.have.property('canSplit', true);
|
||||
});
|
||||
|
||||
it('sets canSplit=false when config says to', function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
const writer = new ResponseWriter(vis, { canSplit: false });
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
canSplit: false,
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
expect(writer).to.have.property('canSplit', false);
|
||||
});
|
||||
|
||||
|
@ -88,7 +48,10 @@ describe('ResponseWriter class', function () {
|
|||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
const partial = Boolean(Math.round(Math.random()));
|
||||
|
||||
const writer = new ResponseWriter(vis, { partialRows: partial });
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical(),
|
||||
partialRows: partial
|
||||
});
|
||||
expect(writer).to.have.property('partialRows', partial);
|
||||
});
|
||||
|
||||
|
@ -97,16 +60,20 @@ describe('ResponseWriter class', function () {
|
|||
const hierarchical = Boolean(Math.round(Math.random()));
|
||||
sinon.stub(vis, 'isHierarchical').returns(hierarchical);
|
||||
|
||||
const writer = new ResponseWriter(vis, {});
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
expect(writer).to.have.property('partialRows', hierarchical);
|
||||
});
|
||||
});
|
||||
|
||||
it('starts off with a root TableGroup', function () {
|
||||
it('starts off with a root TabifyTableGroup', function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
|
||||
const writer = new ResponseWriter(vis);
|
||||
expect(writer.root).to.be.a(TableGroup);
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
expect(writer.root).to.be.a(TabifyTableGroup);
|
||||
expect(writer.splitStack).to.be.an('array');
|
||||
expect(writer.splitStack).to.have.length(1);
|
||||
expect(writer.splitStack[0]).to.be(writer.root);
|
||||
|
@ -117,15 +84,20 @@ describe('ResponseWriter class', function () {
|
|||
defineSetup();
|
||||
|
||||
describe('#response()', function () {
|
||||
it('returns the root TableGroup if splitting', function () {
|
||||
it('returns the root TabifyTableGroup if splitting', function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
const writer = new ResponseWriter(vis);
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
expect(writer.response()).to.be(writer.root);
|
||||
});
|
||||
|
||||
it('returns the first table if not splitting', function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
const writer = new ResponseWriter(vis, { canSplit: false });
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical(),
|
||||
canSplit: false
|
||||
});
|
||||
const table = writer._table();
|
||||
expect(writer.response()).to.be(table);
|
||||
});
|
||||
|
@ -138,8 +110,10 @@ describe('ResponseWriter class', function () {
|
|||
{ type: 'count', schema: 'metric' }
|
||||
]
|
||||
});
|
||||
const buckets = new Buckets({ buckets: [ { key: 'nginx' }, { key: 'apache' } ] });
|
||||
const writer = new ResponseWriter(vis);
|
||||
const buckets = new TabifyBuckets({ buckets: [ { key: 'nginx' }, { key: 'apache' } ] });
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
const tables = [];
|
||||
|
||||
writer.split(vis.aggs[0], buckets, function () {
|
||||
|
@ -153,7 +127,7 @@ describe('ResponseWriter class', function () {
|
|||
});
|
||||
|
||||
const resp = writer.response();
|
||||
expect(resp).to.be.a(TableGroup);
|
||||
expect(resp).to.be.a(TabifyTableGroup);
|
||||
expect(resp.tables).to.have.length(2);
|
||||
|
||||
const nginx = resp.tables.shift();
|
||||
|
@ -190,8 +164,11 @@ describe('ResponseWriter class', function () {
|
|||
]
|
||||
});
|
||||
const agg = vis.aggs.bySchemaName.split[0];
|
||||
const buckets = new Buckets({ buckets: [ { key: 'apache' } ] });
|
||||
const writer = new ResponseWriter(vis, { canSplit: false });
|
||||
const buckets = new TabifyBuckets({ buckets: [ { key: 'apache' } ] });
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical(),
|
||||
canSplit: false
|
||||
});
|
||||
|
||||
expect(function () {
|
||||
writer.split(agg, buckets, _.noop);
|
||||
|
@ -209,10 +186,13 @@ describe('ResponseWriter class', function () {
|
|||
]
|
||||
});
|
||||
|
||||
const writer = new ResponseWriter(vis, { asAggConfigResults: true });
|
||||
const extensions = new Buckets({ buckets: [ { key: 'jpg' }, { key: 'png' } ] });
|
||||
const types = new Buckets({ buckets: [ { key: 'nginx' }, { key: 'apache' } ] });
|
||||
const os = new Buckets({ buckets: [ { key: 'window' }, { key: 'osx' } ] });
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical(),
|
||||
asAggConfigResults: true
|
||||
});
|
||||
const extensions = new TabifyBuckets({ buckets: [ { key: 'jpg' }, { key: 'png' } ] });
|
||||
const types = new TabifyBuckets({ buckets: [ { key: 'nginx' }, { key: 'apache' } ] });
|
||||
const os = new TabifyBuckets({ buckets: [ { key: 'window' }, { key: 'osx' } ] });
|
||||
|
||||
extensions.forEach(function (b, extension) {
|
||||
writer.cell(vis.aggs[0], extension, function () {
|
||||
|
@ -254,10 +234,12 @@ describe('ResponseWriter class', function () {
|
|||
});
|
||||
|
||||
describe('#cell()', function () {
|
||||
it('logs a cell in the ResponseWriters row buffer, calls the block arg, then removes the value from the buffer',
|
||||
it('logs a cell in the TabbedAggResponseWriters row buffer, calls the block arg, then removes the value from the buffer',
|
||||
function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
const writer = new ResponseWriter(vis);
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
expect(writer.rowBuffer).to.have.length(0);
|
||||
writer.cell({}, 500, function () {
|
||||
|
@ -269,9 +251,11 @@ describe('ResponseWriter class', function () {
|
|||
});
|
||||
|
||||
describe('#row()', function () {
|
||||
it('writes the ResponseWriters internal rowBuffer into a table', function () {
|
||||
it('writes the TabbedAggResponseWriters internal rowBuffer into a table', function () {
|
||||
const vis = new Vis(indexPattern, { type: 'histogram', aggs: [] });
|
||||
const writer = new ResponseWriter(vis);
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
const table = writer._table();
|
||||
writer.cell({}, 1, function () {
|
||||
|
@ -299,20 +283,22 @@ describe('ResponseWriter class', function () {
|
|||
const splits = vis.aggs.bySchemaName.split;
|
||||
|
||||
const type = splits[0];
|
||||
const typeBuckets = new Buckets({ buckets: [ { key: 'nginx' }, { key: 'apache' } ] });
|
||||
const typeTabifyBuckets = new TabifyBuckets({ buckets: [ { key: 'nginx' }, { key: 'apache' } ] });
|
||||
|
||||
const ext = splits[1];
|
||||
const extBuckets = new Buckets({ buckets: [ { key: 'jpg' }, { key: 'png' } ] });
|
||||
const extTabifyBuckets = new TabifyBuckets({ buckets: [ { key: 'jpg' }, { key: 'png' } ] });
|
||||
|
||||
const os = splits[2];
|
||||
const osBuckets = new Buckets({ buckets: [ { key: 'windows' }, { key: 'mac' } ] });
|
||||
const osTabifyBuckets = new TabifyBuckets({ buckets: [ { key: 'windows' }, { key: 'mac' } ] });
|
||||
|
||||
const count = vis.aggs[3];
|
||||
|
||||
const writer = new ResponseWriter(vis);
|
||||
writer.split(type, typeBuckets, function () {
|
||||
writer.split(ext, extBuckets, function () {
|
||||
writer.split(os, osBuckets, function (bucket, key) {
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
writer.split(type, typeTabifyBuckets, function () {
|
||||
writer.split(ext, extTabifyBuckets, function () {
|
||||
writer.split(os, osTabifyBuckets, function (bucket, key) {
|
||||
writer.cell(count, key === 'windows' ? 1 : 2, function () {
|
||||
writer.row();
|
||||
});
|
||||
|
@ -353,7 +339,9 @@ describe('ResponseWriter class', function () {
|
|||
]
|
||||
});
|
||||
|
||||
const writer = new ResponseWriter(vis);
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
const table = writer._table();
|
||||
writer.cell(vis.aggs[0], 'apache', function () {
|
||||
writer.row();
|
||||
|
@ -372,7 +360,9 @@ describe('ResponseWriter class', function () {
|
|||
]
|
||||
});
|
||||
|
||||
const writer = new ResponseWriter(vis);
|
||||
const writer = new TabbedAggResponseWriter(vis.getAggConfig().getResponseAggs(), {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
const table = writer._table();
|
||||
writer.cell(vis.aggs[0], 'apache', function () {
|
||||
writer.row();
|
||||
|
|
|
@ -1,26 +1,17 @@
|
|||
import _ from 'lodash';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { AggResponseTabifyTableProvider } from 'ui/agg_response/tabify/_table';
|
||||
|
||||
describe('Table class', function () {
|
||||
|
||||
let Table;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
Table = Private(AggResponseTabifyTableProvider);
|
||||
}));
|
||||
import { TabifyTable } from 'ui/agg_response/tabify/_table';
|
||||
|
||||
describe('TabifyTable class', function () {
|
||||
it('exposes rows array, but not the columns', function () {
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
expect(table.rows).to.be.an('array');
|
||||
expect(table.columns == null).to.be.ok();
|
||||
});
|
||||
|
||||
describe('#aggConfig', function () {
|
||||
it('accepts a column from the table and returns its agg config', function () {
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
const football = {};
|
||||
const column = {
|
||||
aggConfig: football
|
||||
|
@ -32,31 +23,31 @@ describe('Table class', function () {
|
|||
it('throws a TypeError if the column is malformed', function () {
|
||||
expect(function () {
|
||||
const notAColumn = {};
|
||||
(new Table()).aggConfig(notAColumn);
|
||||
(new TabifyTable()).aggConfig(notAColumn);
|
||||
}).to.throwException(TypeError);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#title', function () {
|
||||
it('returns nothing if the table is not part of a table group', function () {
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
expect(table.title()).to.be('');
|
||||
});
|
||||
|
||||
it('returns the title of the TableGroup if the table is part of one', function () {
|
||||
const table = new Table();
|
||||
it('returns the title of the TabifyTableGroup if the table is part of one', function () {
|
||||
const table = new TabifyTable();
|
||||
table.$parent = {
|
||||
title: 'TableGroup Title',
|
||||
title: 'TabifyTableGroup Title',
|
||||
tables: [table]
|
||||
};
|
||||
|
||||
expect(table.title()).to.be('TableGroup Title');
|
||||
expect(table.title()).to.be('TabifyTableGroup Title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#field', function () {
|
||||
it('calls the columns aggConfig#getField() method', function () {
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
const football = {};
|
||||
const column = {
|
||||
aggConfig: {
|
||||
|
@ -70,7 +61,7 @@ describe('Table class', function () {
|
|||
|
||||
describe('#fieldFormatter', function () {
|
||||
it('calls the columns aggConfig#fieldFormatter() method', function () {
|
||||
const table = new Table();
|
||||
const table = new TabifyTable();
|
||||
const football = {};
|
||||
const column = {
|
||||
aggConfig: {
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
import { AggResponseTabifyTableGroupProvider } from 'ui/agg_response/tabify/_table_group';
|
||||
import { TabifyTableGroup } from 'ui/agg_response/tabify/_table_group';
|
||||
|
||||
describe('Table Group class', function () {
|
||||
let TableGroup;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
TableGroup = Private(AggResponseTabifyTableGroupProvider);
|
||||
}));
|
||||
|
||||
it('exposes tables array and empty aggConfig, key and title', function () {
|
||||
const tableGroup = new TableGroup();
|
||||
const tableGroup = new TabifyTableGroup();
|
||||
expect(tableGroup.tables).to.be.an('array');
|
||||
expect(tableGroup.aggConfig).to.be(null);
|
||||
expect(tableGroup.key).to.be(null);
|
||||
|
|
|
@ -1,68 +1,65 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
export function AggResponseBucketsProvider() {
|
||||
|
||||
function Buckets(aggResp, aggParams) {
|
||||
if (_.has(aggResp, 'buckets')) {
|
||||
this.buckets = aggResp.buckets;
|
||||
} else if (aggResp) {
|
||||
// Some Bucket Aggs only return a single bucket (like filter).
|
||||
// In those instances, the aggResp is the content of the single bucket.
|
||||
this.buckets = [aggResp];
|
||||
} else {
|
||||
this.buckets = [];
|
||||
}
|
||||
|
||||
this.objectMode = _.isPlainObject(this.buckets);
|
||||
if (this.objectMode) {
|
||||
this._keys = _.keys(this.buckets);
|
||||
this.length = this._keys.length;
|
||||
} else {
|
||||
this.length = this.buckets.length;
|
||||
}
|
||||
|
||||
if (this.length && aggParams) this._orderBucketsAccordingToParams(aggParams);
|
||||
function TabifyBuckets(aggResp, aggParams) {
|
||||
if (_.has(aggResp, 'buckets')) {
|
||||
this.buckets = aggResp.buckets;
|
||||
} else if (aggResp) {
|
||||
// Some Bucket Aggs only return a single bucket (like filter).
|
||||
// In those instances, the aggResp is the content of the single bucket.
|
||||
this.buckets = [aggResp];
|
||||
} else {
|
||||
this.buckets = [];
|
||||
}
|
||||
|
||||
Buckets.prototype.forEach = function (fn) {
|
||||
const buckets = this.buckets;
|
||||
this.objectMode = _.isPlainObject(this.buckets);
|
||||
if (this.objectMode) {
|
||||
this._keys = _.keys(this.buckets);
|
||||
this.length = this._keys.length;
|
||||
} else {
|
||||
this.length = this.buckets.length;
|
||||
}
|
||||
|
||||
if (this.objectMode) {
|
||||
this._keys.forEach(function (key) {
|
||||
fn(buckets[key], key);
|
||||
});
|
||||
} else {
|
||||
buckets.forEach(function (bucket) {
|
||||
fn(bucket, bucket.key);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Buckets.prototype._isRangeEqual = function (range1, range2) {
|
||||
return _.get(range1, 'from', null) === _.get(range2, 'from', null)
|
||||
&& _.get(range1, 'to', null) === _.get(range2, 'to', null);
|
||||
};
|
||||
|
||||
Buckets.prototype._orderBucketsAccordingToParams = function (params) {
|
||||
if (params.filters && this.objectMode) {
|
||||
this._keys = params.filters.map(filter => {
|
||||
return filter.label || filter.input.query || '*';
|
||||
});
|
||||
} else if (params.ranges && this.objectMode) {
|
||||
this._keys = params.ranges.map(range => {
|
||||
return _.findKey(this.buckets, el => this._isRangeEqual(el, range));
|
||||
});
|
||||
} else if (params.ranges && params.field.type !== 'date') {
|
||||
let ranges = params.ranges;
|
||||
if (params.ipRangeType) {
|
||||
ranges = params.ipRangeType === 'mask' ? ranges.mask : ranges.fromTo;
|
||||
}
|
||||
this.buckets = ranges.map(range => {
|
||||
if (range.mask) return this.buckets.find(el => el.key === range.mask);
|
||||
return this.buckets.find(el => this._isRangeEqual(el, range));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return Buckets;
|
||||
if (this.length && aggParams) this._orderBucketsAccordingToParams(aggParams);
|
||||
}
|
||||
|
||||
TabifyBuckets.prototype.forEach = function (fn) {
|
||||
const buckets = this.buckets;
|
||||
|
||||
if (this.objectMode) {
|
||||
this._keys.forEach(function (key) {
|
||||
fn(buckets[key], key);
|
||||
});
|
||||
} else {
|
||||
buckets.forEach(function (bucket) {
|
||||
fn(bucket, bucket.key);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
TabifyBuckets.prototype._isRangeEqual = function (range1, range2) {
|
||||
return _.get(range1, 'from', null) === _.get(range2, 'from', null)
|
||||
&& _.get(range1, 'to', null) === _.get(range2, 'to', null);
|
||||
};
|
||||
|
||||
TabifyBuckets.prototype._orderBucketsAccordingToParams = function (params) {
|
||||
if (params.filters && this.objectMode) {
|
||||
this._keys = params.filters.map(filter => {
|
||||
return filter.label || filter.input.query || '*';
|
||||
});
|
||||
} else if (params.ranges && this.objectMode) {
|
||||
this._keys = params.ranges.map(range => {
|
||||
return _.findKey(this.buckets, el => this._isRangeEqual(el, range));
|
||||
});
|
||||
} else if (params.ranges && params.field.type !== 'date') {
|
||||
let ranges = params.ranges;
|
||||
if (params.ipRangeType) {
|
||||
ranges = params.ipRangeType === 'mask' ? ranges.mask : ranges.fromTo;
|
||||
}
|
||||
this.buckets = ranges.map(range => {
|
||||
if (range.mask) return this.buckets.find(el => el.key === range.mask);
|
||||
return this.buckets.find(el => this._isRangeEqual(el, range));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export { TabifyBuckets };
|
||||
|
|
|
@ -1,51 +1,38 @@
|
|||
import _ from 'lodash';
|
||||
import { VisAggConfigProvider } from 'ui/vis/agg_config';
|
||||
|
||||
export function AggResponseGetColumnsProvider(Private) {
|
||||
const AggConfig = Private(VisAggConfigProvider);
|
||||
export function tabifyGetColumns(aggs, minimal, hierarchical) {
|
||||
|
||||
return function getColumns(vis, minimal) {
|
||||
const aggs = vis.getAggConfig().getResponseAggs();
|
||||
if (minimal == null) minimal = !hierarchical;
|
||||
|
||||
if (minimal == null) minimal = !vis.isHierarchical();
|
||||
|
||||
if (!vis.getAggConfig().bySchemaGroup.metrics) {
|
||||
aggs.push(new AggConfig(vis, {
|
||||
type: 'count',
|
||||
schema: vis.type.schemas.metrics[0].name
|
||||
}));
|
||||
}
|
||||
|
||||
// pick the columns
|
||||
if (minimal) {
|
||||
return aggs.map(function (agg) {
|
||||
return { aggConfig: agg };
|
||||
});
|
||||
}
|
||||
|
||||
// supposed to be bucket,...metrics,bucket,...metrics
|
||||
const columns = [];
|
||||
|
||||
// seperate the metrics
|
||||
const grouped = _.groupBy(aggs, function (agg) {
|
||||
return agg.schema.group;
|
||||
// pick the columns
|
||||
if (minimal) {
|
||||
return aggs.map(function (agg) {
|
||||
return { aggConfig: agg };
|
||||
});
|
||||
}
|
||||
|
||||
if (!grouped.buckets) {
|
||||
// return just the metrics, in column format
|
||||
return grouped.metrics.map(function (agg) {
|
||||
return { aggConfig: agg };
|
||||
});
|
||||
}
|
||||
// supposed to be bucket,...metrics,bucket,...metrics
|
||||
const columns = [];
|
||||
|
||||
// return the buckets, and after each place all of the metrics
|
||||
grouped.buckets.forEach(function (agg) {
|
||||
columns.push({ aggConfig: agg });
|
||||
grouped.metrics.forEach(function (metric) {
|
||||
columns.push({ aggConfig: metric });
|
||||
});
|
||||
// seperate the metrics
|
||||
const grouped = _.groupBy(aggs, function (agg) {
|
||||
return agg.schema.group;
|
||||
});
|
||||
|
||||
if (!grouped.buckets) {
|
||||
// return just the metrics, in column format
|
||||
return grouped.metrics.map(function (agg) {
|
||||
return { aggConfig: agg };
|
||||
});
|
||||
}
|
||||
|
||||
return columns;
|
||||
};
|
||||
// return the buckets, and after each place all of the metrics
|
||||
grouped.buckets.forEach(function (agg) {
|
||||
columns.push({ aggConfig: agg });
|
||||
grouped.metrics.forEach(function (metric) {
|
||||
columns.push({ aggConfig: metric });
|
||||
});
|
||||
});
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
|
|
@ -1,288 +1,280 @@
|
|||
import _ from 'lodash';
|
||||
import AggConfigResult from 'ui/vis/agg_config_result';
|
||||
import { AggResponseTabifyTableProvider } from 'ui/agg_response/tabify/_table';
|
||||
import { AggResponseTabifyTableGroupProvider } from 'ui/agg_response/tabify/_table_group';
|
||||
import { AggResponseGetColumnsProvider } from 'ui/agg_response/tabify/_get_columns';
|
||||
import { TabifyTable } from 'ui/agg_response/tabify/_table';
|
||||
import { TabifyTableGroup } from 'ui/agg_response/tabify/_table_group';
|
||||
import { tabifyGetColumns } from 'ui/agg_response/tabify/_get_columns';
|
||||
|
||||
export function TabbedAggResponseWriterProvider(Private) {
|
||||
const Table = Private(AggResponseTabifyTableProvider);
|
||||
const TableGroup = Private(AggResponseTabifyTableGroupProvider);
|
||||
const getColumns = Private(AggResponseGetColumnsProvider);
|
||||
|
||||
|
||||
_.class(SplitAcr).inherits(AggConfigResult);
|
||||
function SplitAcr(agg, parent, key) {
|
||||
SplitAcr.Super.call(this, agg, parent, key, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writer class that collects information about an aggregation response and
|
||||
* produces a table, or a series of tables.
|
||||
*
|
||||
* @param {Vis} vis - the vis object to which the aggregation response correlates
|
||||
*/
|
||||
function TabbedAggResponseWriter(vis, opts) {
|
||||
this.vis = vis;
|
||||
this.opts = opts || {};
|
||||
this.rowBuffer = [];
|
||||
|
||||
const visIsHier = vis.isHierarchical();
|
||||
|
||||
// do the options allow for splitting? we will only split if true and
|
||||
// tabify calls the split method.
|
||||
this.canSplit = this.opts.canSplit !== false;
|
||||
|
||||
// should we allow partial rows to be included in the tables? if a
|
||||
// partial row is found, it is filled with empty strings ''
|
||||
this.partialRows = this.opts.partialRows == null ? visIsHier : this.opts.partialRows;
|
||||
|
||||
// if true, we will not place metric columns after every bucket
|
||||
// even if the vis is hierarchical. if false, and the vis is
|
||||
// hierarchical, then we will display metric columns after
|
||||
// every bucket col
|
||||
this.minimalColumns = visIsHier ? !!this.opts.minimalColumns : true;
|
||||
|
||||
// true if we can expect metrics to have been calculated
|
||||
// for every bucket
|
||||
this.metricsForAllBuckets = visIsHier;
|
||||
|
||||
// if true, values will be wrapped in aggConfigResult objects which link them
|
||||
// to their aggConfig and enable the filterbar and tooltip formatters
|
||||
this.asAggConfigResults = !!this.opts.asAggConfigResults;
|
||||
|
||||
this.columns = getColumns(vis, this.minimalColumns);
|
||||
this.aggStack = _.pluck(this.columns, 'aggConfig');
|
||||
|
||||
this.root = new TableGroup();
|
||||
this.acrStack = [];
|
||||
this.splitStack = [this.root];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Table of TableGroup object, link it to it's parent (if any), and determine if
|
||||
* it's the root
|
||||
*
|
||||
* @param {boolean} group - is this a TableGroup or just a normal Table
|
||||
* @param {AggConfig} agg - the aggregation that create this table, only applies to groups
|
||||
* @param {any} key - the bucketKey that this table relates to
|
||||
* @return {Table/TableGroup} table - the created table
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype._table = function (group, agg, key) {
|
||||
const Class = (group) ? TableGroup : Table;
|
||||
const table = new Class();
|
||||
const parent = this.splitStack[0];
|
||||
|
||||
if (group) {
|
||||
table.aggConfig = agg;
|
||||
table.key = key;
|
||||
table.title = (table.fieldFormatter()(key));
|
||||
// aggs that don't implement makeLabel should not add to title
|
||||
if (agg.makeLabel() !== agg.name) {
|
||||
table.title += ': ' + agg.makeLabel();
|
||||
}
|
||||
}
|
||||
|
||||
// link the parent and child
|
||||
table.$parent = parent;
|
||||
parent.tables.push(table);
|
||||
|
||||
return table;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enter into a split table, called for each bucket of a splitting agg. The new table
|
||||
* is either created or located using the agg and key arguments, and then the block is
|
||||
* executed with the table as it's this context. Within this function, you should
|
||||
* walk into the remaining branches and end up writing some rows to the table.
|
||||
*
|
||||
* @param {aggConfig} agg - the aggConfig that created this split
|
||||
* @param {Buckets} buckets - the buckets produces by the agg
|
||||
* @param {function} block - a function to execute for each sub bucket
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.split = function (agg, buckets, block) {
|
||||
const self = this;
|
||||
|
||||
if (!self.canSplit) {
|
||||
throw new Error('attempted to split when splitting is disabled');
|
||||
}
|
||||
|
||||
self._removeAggFromColumns(agg);
|
||||
|
||||
buckets.forEach(function (bucket, key) {
|
||||
// find the existing split that we should extend
|
||||
let tableGroup = _.find(self.splitStack[0].tables, { aggConfig: agg, key: key });
|
||||
// create the split if it doesn't exist yet
|
||||
if (!tableGroup) tableGroup = self._table(true, agg, key);
|
||||
|
||||
let splitAcr = false;
|
||||
if (self.asAggConfigResults) {
|
||||
splitAcr = self._injectParentSplit(agg, key);
|
||||
}
|
||||
|
||||
// push the split onto the stack so that it will receive written tables
|
||||
self.splitStack.unshift(tableGroup);
|
||||
|
||||
// call the block
|
||||
if (_.isFunction(block)) block.call(self, bucket, key);
|
||||
|
||||
// remove the split from the stack
|
||||
self.splitStack.shift();
|
||||
splitAcr && _.pull(self.acrStack, splitAcr);
|
||||
});
|
||||
};
|
||||
|
||||
TabbedAggResponseWriter.prototype._removeAggFromColumns = function (agg) {
|
||||
const i = _.findIndex(this.columns, function (col) {
|
||||
return col.aggConfig === agg;
|
||||
});
|
||||
|
||||
// we must have already removed this column
|
||||
if (i === -1) return;
|
||||
|
||||
this.columns.splice(i, 1);
|
||||
|
||||
if (this.minimalColumns) return;
|
||||
|
||||
// hierarchical vis creats additional columns for each bucket
|
||||
// we will remove those too
|
||||
const mCol = this.columns.splice(i, 1).pop();
|
||||
const mI = _.findIndex(this.aggStack, function (agg) {
|
||||
return agg === mCol.aggConfig;
|
||||
});
|
||||
|
||||
if (mI > -1) this.aggStack.splice(mI, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* When a split is found while building the aggConfigResult tree, we
|
||||
* want to push the split into the tree at another point. Since each
|
||||
* branch in the tree is a double-linked list we need do some special
|
||||
* shit to pull this off.
|
||||
*
|
||||
* @private
|
||||
* @param {AggConfig} - The agg which produced the split bucket
|
||||
* @param {any} - The value which identifies the bucket
|
||||
* @return {SplitAcr} - the AggConfigResult created for the split bucket
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype._injectParentSplit = function (agg, key) {
|
||||
const oldList = this.acrStack;
|
||||
const newList = this.acrStack = [];
|
||||
|
||||
// walk from right to left through the old stack
|
||||
// and move things to the new stack
|
||||
let injected = false;
|
||||
|
||||
if (!oldList.length) {
|
||||
injected = new SplitAcr(agg, null, key);
|
||||
newList.unshift(injected);
|
||||
return injected;
|
||||
}
|
||||
|
||||
// walk from right to left, emptying the previous list
|
||||
while (oldList.length) {
|
||||
const acr = oldList.pop();
|
||||
|
||||
// ignore other splits
|
||||
if (acr instanceof SplitAcr) {
|
||||
newList.unshift(acr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// inject the split
|
||||
if (!injected) {
|
||||
injected = new SplitAcr(agg, newList[0], key);
|
||||
newList.unshift(injected);
|
||||
}
|
||||
|
||||
const newAcr = new AggConfigResult(acr.aggConfig, newList[0], acr.value, acr.aggConfig.getKey(acr), acr.filters);
|
||||
newList.unshift(newAcr);
|
||||
|
||||
// and replace the acr in the row buffer if its there
|
||||
const rowI = this.rowBuffer.indexOf(acr);
|
||||
if (rowI > -1) {
|
||||
this.rowBuffer[rowI] = newAcr;
|
||||
}
|
||||
}
|
||||
|
||||
return injected;
|
||||
};
|
||||
|
||||
/**
|
||||
* Push a value into the row, then run a block. Once the block is
|
||||
* complete the value is pulled from the stack.
|
||||
*
|
||||
* @param {any} value - the value that should be added to the row
|
||||
* @param {function} block - the function to run while this value is in the row
|
||||
* @return {any} - the value that was added
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.cell = function (agg, value, block, filters) {
|
||||
if (this.asAggConfigResults) {
|
||||
value = new AggConfigResult(agg, this.acrStack[0], value, value, filters);
|
||||
}
|
||||
|
||||
const staskResult = this.asAggConfigResults && value.type === 'bucket';
|
||||
|
||||
this.rowBuffer.push(value);
|
||||
if (staskResult) this.acrStack.unshift(value);
|
||||
|
||||
if (_.isFunction(block)) block.call(this);
|
||||
|
||||
this.rowBuffer.pop(value);
|
||||
if (staskResult) this.acrStack.shift();
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new row by reading the row buffer. This will do nothing if
|
||||
* the row is incomplete and the vis this data came from is NOT flagged as
|
||||
* hierarchical.
|
||||
*
|
||||
* @param {array} [buffer] - optional buffer to use in place of the stored rowBuffer
|
||||
* @return {undefined}
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.row = function (buffer) {
|
||||
const cells = buffer || this.rowBuffer.slice(0);
|
||||
|
||||
if (!this.partialRows && cells.length < this.columns.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const split = this.splitStack[0];
|
||||
const table = split.tables[0] || this._table(false);
|
||||
|
||||
while (cells.length < this.columns.length) cells.push('');
|
||||
table.rows.push(cells);
|
||||
return table;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the actual response
|
||||
*
|
||||
* @return {object} - the final table-tree
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.response = function () {
|
||||
const columns = this.columns;
|
||||
|
||||
// give the columns some metadata
|
||||
columns.map(function (col) {
|
||||
col.title = col.aggConfig.makeLabel();
|
||||
});
|
||||
|
||||
// walk the tree and write the columns to each table
|
||||
((function step(table) {
|
||||
if (table.tables) table.tables.forEach(step);
|
||||
else table.columns = columns.slice(0);
|
||||
})(this.root));
|
||||
|
||||
if (this.canSplit) return this.root;
|
||||
|
||||
const table = this.root.tables[0];
|
||||
if (!table) return;
|
||||
|
||||
delete table.$parent;
|
||||
return table;
|
||||
};
|
||||
|
||||
return TabbedAggResponseWriter;
|
||||
_.class(SplitAcr).inherits(AggConfigResult);
|
||||
function SplitAcr(agg, parent, key) {
|
||||
SplitAcr.Super.call(this, agg, parent, key, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writer class that collects information about an aggregation response and
|
||||
* produces a table, or a series of tables.
|
||||
*
|
||||
* @param {Vis} vis - the vis object to which the aggregation response correlates
|
||||
*/
|
||||
function TabbedAggResponseWriter(aggs, opts) {
|
||||
this.opts = opts || {};
|
||||
this.rowBuffer = [];
|
||||
|
||||
const visIsHier = opts.isHierarchical;
|
||||
|
||||
// do the options allow for splitting? we will only split if true and
|
||||
// tabify calls the split method.
|
||||
this.canSplit = this.opts.canSplit !== false;
|
||||
|
||||
// should we allow partial rows to be included in the tables? if a
|
||||
// partial row is found, it is filled with empty strings ''
|
||||
this.partialRows = this.opts.partialRows == null ? visIsHier : this.opts.partialRows;
|
||||
|
||||
// if true, we will not place metric columns after every bucket
|
||||
// even if the vis is hierarchical. if false, and the vis is
|
||||
// hierarchical, then we will display metric columns after
|
||||
// every bucket col
|
||||
this.minimalColumns = visIsHier ? !!this.opts.minimalColumns : true;
|
||||
|
||||
// true if we can expect metrics to have been calculated
|
||||
// for every bucket
|
||||
this.metricsForAllBuckets = visIsHier;
|
||||
|
||||
// if true, values will be wrapped in aggConfigResult objects which link them
|
||||
// to their aggConfig and enable the filterbar and tooltip formatters
|
||||
this.asAggConfigResults = !!this.opts.asAggConfigResults;
|
||||
|
||||
this.columns = tabifyGetColumns(aggs, this.minimalColumns);
|
||||
this.aggStack = _.pluck(this.columns, 'aggConfig');
|
||||
|
||||
this.root = new TabifyTableGroup();
|
||||
this.acrStack = [];
|
||||
this.splitStack = [this.root];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Table of TableGroup object, link it to it's parent (if any), and determine if
|
||||
* it's the root
|
||||
*
|
||||
* @param {boolean} group - is this a TableGroup or just a normal Table
|
||||
* @param {AggConfig} agg - the aggregation that create this table, only applies to groups
|
||||
* @param {any} key - the bucketKey that this table relates to
|
||||
* @return {Table/TableGroup} table - the created table
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype._table = function (group, agg, key) {
|
||||
const Class = (group) ? TabifyTableGroup : TabifyTable;
|
||||
const table = new Class();
|
||||
const parent = this.splitStack[0];
|
||||
|
||||
if (group) {
|
||||
table.aggConfig = agg;
|
||||
table.key = key;
|
||||
table.title = (table.fieldFormatter()(key));
|
||||
// aggs that don't implement makeLabel should not add to title
|
||||
if (agg.makeLabel() !== agg.name) {
|
||||
table.title += ': ' + agg.makeLabel();
|
||||
}
|
||||
}
|
||||
|
||||
// link the parent and child
|
||||
table.$parent = parent;
|
||||
parent.tables.push(table);
|
||||
|
||||
return table;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enter into a split table, called for each bucket of a splitting agg. The new table
|
||||
* is either created or located using the agg and key arguments, and then the block is
|
||||
* executed with the table as it's this context. Within this function, you should
|
||||
* walk into the remaining branches and end up writing some rows to the table.
|
||||
*
|
||||
* @param {aggConfig} agg - the aggConfig that created this split
|
||||
* @param {Buckets} buckets - the buckets produces by the agg
|
||||
* @param {function} block - a function to execute for each sub bucket
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.split = function (agg, buckets, block) {
|
||||
const self = this;
|
||||
|
||||
if (!self.canSplit) {
|
||||
throw new Error('attempted to split when splitting is disabled');
|
||||
}
|
||||
|
||||
self._removeAggFromColumns(agg);
|
||||
|
||||
buckets.forEach(function (bucket, key) {
|
||||
// find the existing split that we should extend
|
||||
let tableGroup = _.find(self.splitStack[0].tables, { aggConfig: agg, key: key });
|
||||
// create the split if it doesn't exist yet
|
||||
if (!tableGroup) tableGroup = self._table(true, agg, key);
|
||||
|
||||
let splitAcr = false;
|
||||
if (self.asAggConfigResults) {
|
||||
splitAcr = self._injectParentSplit(agg, key);
|
||||
}
|
||||
|
||||
// push the split onto the stack so that it will receive written tables
|
||||
self.splitStack.unshift(tableGroup);
|
||||
|
||||
// call the block
|
||||
if (_.isFunction(block)) block.call(self, bucket, key);
|
||||
|
||||
// remove the split from the stack
|
||||
self.splitStack.shift();
|
||||
splitAcr && _.pull(self.acrStack, splitAcr);
|
||||
});
|
||||
};
|
||||
|
||||
TabbedAggResponseWriter.prototype._removeAggFromColumns = function (agg) {
|
||||
const i = _.findIndex(this.columns, function (col) {
|
||||
return col.aggConfig === agg;
|
||||
});
|
||||
|
||||
// we must have already removed this column
|
||||
if (i === -1) return;
|
||||
|
||||
this.columns.splice(i, 1);
|
||||
|
||||
if (this.minimalColumns) return;
|
||||
|
||||
// hierarchical vis creats additional columns for each bucket
|
||||
// we will remove those too
|
||||
const mCol = this.columns.splice(i, 1).pop();
|
||||
const mI = _.findIndex(this.aggStack, function (agg) {
|
||||
return agg === mCol.aggConfig;
|
||||
});
|
||||
|
||||
if (mI > -1) this.aggStack.splice(mI, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* When a split is found while building the aggConfigResult tree, we
|
||||
* want to push the split into the tree at another point. Since each
|
||||
* branch in the tree is a double-linked list we need do some special
|
||||
* shit to pull this off.
|
||||
*
|
||||
* @private
|
||||
* @param {AggConfig} - The agg which produced the split bucket
|
||||
* @param {any} - The value which identifies the bucket
|
||||
* @return {SplitAcr} - the AggConfigResult created for the split bucket
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype._injectParentSplit = function (agg, key) {
|
||||
const oldList = this.acrStack;
|
||||
const newList = this.acrStack = [];
|
||||
|
||||
// walk from right to left through the old stack
|
||||
// and move things to the new stack
|
||||
let injected = false;
|
||||
|
||||
if (!oldList.length) {
|
||||
injected = new SplitAcr(agg, null, key);
|
||||
newList.unshift(injected);
|
||||
return injected;
|
||||
}
|
||||
|
||||
// walk from right to left, emptying the previous list
|
||||
while (oldList.length) {
|
||||
const acr = oldList.pop();
|
||||
|
||||
// ignore other splits
|
||||
if (acr instanceof SplitAcr) {
|
||||
newList.unshift(acr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// inject the split
|
||||
if (!injected) {
|
||||
injected = new SplitAcr(agg, newList[0], key);
|
||||
newList.unshift(injected);
|
||||
}
|
||||
|
||||
const newAcr = new AggConfigResult(acr.aggConfig, newList[0], acr.value, acr.aggConfig.getKey(acr), acr.filters);
|
||||
newList.unshift(newAcr);
|
||||
|
||||
// and replace the acr in the row buffer if its there
|
||||
const rowI = this.rowBuffer.indexOf(acr);
|
||||
if (rowI > -1) {
|
||||
this.rowBuffer[rowI] = newAcr;
|
||||
}
|
||||
}
|
||||
|
||||
return injected;
|
||||
};
|
||||
|
||||
/**
|
||||
* Push a value into the row, then run a block. Once the block is
|
||||
* complete the value is pulled from the stack.
|
||||
*
|
||||
* @param {any} value - the value that should be added to the row
|
||||
* @param {function} block - the function to run while this value is in the row
|
||||
* @return {any} - the value that was added
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.cell = function (agg, value, block, filters) {
|
||||
if (this.asAggConfigResults) {
|
||||
value = new AggConfigResult(agg, this.acrStack[0], value, value, filters);
|
||||
}
|
||||
|
||||
const staskResult = this.asAggConfigResults && value.type === 'bucket';
|
||||
|
||||
this.rowBuffer.push(value);
|
||||
if (staskResult) this.acrStack.unshift(value);
|
||||
|
||||
if (_.isFunction(block)) block.call(this);
|
||||
|
||||
this.rowBuffer.pop(value);
|
||||
if (staskResult) this.acrStack.shift();
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new row by reading the row buffer. This will do nothing if
|
||||
* the row is incomplete and the vis this data came from is NOT flagged as
|
||||
* hierarchical.
|
||||
*
|
||||
* @param {array} [buffer] - optional buffer to use in place of the stored rowBuffer
|
||||
* @return {undefined}
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.row = function (buffer) {
|
||||
const cells = buffer || this.rowBuffer.slice(0);
|
||||
|
||||
if (!this.partialRows && cells.length < this.columns.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const split = this.splitStack[0];
|
||||
const table = split.tables[0] || this._table(false);
|
||||
|
||||
while (cells.length < this.columns.length) cells.push('');
|
||||
table.rows.push(cells);
|
||||
return table;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the actual response
|
||||
*
|
||||
* @return {object} - the final table-tree
|
||||
*/
|
||||
TabbedAggResponseWriter.prototype.response = function () {
|
||||
const columns = this.columns;
|
||||
|
||||
// give the columns some metadata
|
||||
columns.map(function (col) {
|
||||
col.title = col.aggConfig.makeLabel();
|
||||
});
|
||||
|
||||
// walk the tree and write the columns to each table
|
||||
((function step(table) {
|
||||
if (table.tables) table.tables.forEach(step);
|
||||
else table.columns = columns.slice(0);
|
||||
})(this.root));
|
||||
|
||||
if (this.canSplit) return this.root;
|
||||
|
||||
const table = this.root.tables[0];
|
||||
if (!table) return;
|
||||
|
||||
delete table.$parent;
|
||||
return table;
|
||||
};
|
||||
|
||||
export { TabbedAggResponseWriter };
|
||||
|
|
|
@ -1,37 +1,34 @@
|
|||
|
||||
export function AggResponseTabifyTableProvider() {
|
||||
/**
|
||||
* Simple table class that is used to contain the rows and columns that create
|
||||
* a table. This is usually found at the root of the response or within a TableGroup
|
||||
*/
|
||||
function Table() {
|
||||
this.columns = null; // written with the first row
|
||||
this.rows = [];
|
||||
}
|
||||
|
||||
Table.prototype.title = function () {
|
||||
if (this.$parent) {
|
||||
return this.$parent.title;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
Table.prototype.aggConfig = function (col) {
|
||||
if (!col.aggConfig) {
|
||||
throw new TypeError('Column is missing the aggConfig property');
|
||||
}
|
||||
return col.aggConfig;
|
||||
};
|
||||
|
||||
Table.prototype.field = function (col) {
|
||||
return this.aggConfig(col).getField();
|
||||
};
|
||||
|
||||
Table.prototype.fieldFormatter = function (col) {
|
||||
return this.aggConfig(col).fieldFormatter();
|
||||
};
|
||||
|
||||
|
||||
return Table;
|
||||
/**
|
||||
* Simple table class that is used to contain the rows and columns that create
|
||||
* a table. This is usually found at the root of the response or within a TableGroup
|
||||
*/
|
||||
function TabifyTable() {
|
||||
this.columns = null; // written with the first row
|
||||
this.rows = [];
|
||||
}
|
||||
|
||||
TabifyTable.prototype.title = function () {
|
||||
if (this.$parent) {
|
||||
return this.$parent.title;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
};
|
||||
|
||||
TabifyTable.prototype.aggConfig = function (col) {
|
||||
if (!col.aggConfig) {
|
||||
throw new TypeError('Column is missing the aggConfig property');
|
||||
}
|
||||
return col.aggConfig;
|
||||
};
|
||||
|
||||
TabifyTable.prototype.field = function (col) {
|
||||
return this.aggConfig(col).getField();
|
||||
};
|
||||
|
||||
TabifyTable.prototype.fieldFormatter = function (col) {
|
||||
return this.aggConfig(col).fieldFormatter();
|
||||
};
|
||||
|
||||
|
||||
export { TabifyTable };
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
|
||||
export function AggResponseTabifyTableGroupProvider() {
|
||||
/**
|
||||
* Simple object that wraps multiple tables. It contains information about the aggConfig
|
||||
* and bucket that created this group and a list of the tables within it.
|
||||
*/
|
||||
function TableGroup() {
|
||||
this.aggConfig = null;
|
||||
this.key = null;
|
||||
this.title = null;
|
||||
this.tables = [];
|
||||
}
|
||||
|
||||
TableGroup.prototype.field = function () {
|
||||
if (this.aggConfig) return this.aggConfig.getField();
|
||||
};
|
||||
|
||||
TableGroup.prototype.fieldFormatter = function () {
|
||||
if (this.aggConfig) return this.aggConfig.fieldFormatter();
|
||||
};
|
||||
|
||||
return TableGroup;
|
||||
/**
|
||||
* Simple object that wraps multiple tables. It contains information about the aggConfig
|
||||
* and bucket that created this group and a list of the tables within it.
|
||||
*/
|
||||
function TabifyTableGroup() {
|
||||
this.aggConfig = null;
|
||||
this.key = null;
|
||||
this.title = null;
|
||||
this.tables = [];
|
||||
}
|
||||
|
||||
TabifyTableGroup.prototype.field = function () {
|
||||
if (this.aggConfig) return this.aggConfig.getField();
|
||||
};
|
||||
|
||||
TabifyTableGroup.prototype.fieldFormatter = function () {
|
||||
if (this.aggConfig) return this.aggConfig.fieldFormatter();
|
||||
};
|
||||
|
||||
export { TabifyTableGroup };
|
||||
|
|
|
@ -1 +1 @@
|
|||
export { AggResponseTabifyProvider } from './tabify';
|
||||
export { tabifyAggResponse } from './tabify';
|
||||
|
|
|
@ -1,109 +1,102 @@
|
|||
import _ from 'lodash';
|
||||
import { TabbedAggResponseWriterProvider } from 'ui/agg_response/tabify/_response_writer';
|
||||
import { AggResponseBucketsProvider } from 'ui/agg_response/tabify/_buckets';
|
||||
import { TabbedAggResponseWriter } from 'ui/agg_response/tabify/_response_writer';
|
||||
import { TabifyBuckets } from 'ui/agg_response/tabify/_buckets';
|
||||
|
||||
export function AggResponseTabifyProvider(Private, Notifier) {
|
||||
const TabbedAggResponseWriter = Private(TabbedAggResponseWriterProvider);
|
||||
const Buckets = Private(AggResponseBucketsProvider);
|
||||
const notify = new Notifier({ location: 'agg_response/tabify' });
|
||||
export function tabifyAggResponse(aggs, esResponse, respOpts = {}) {
|
||||
const write = new TabbedAggResponseWriter(aggs, respOpts);
|
||||
|
||||
function tabifyAggResponse(vis, esResponse, respOpts) {
|
||||
const write = new TabbedAggResponseWriter(vis, respOpts);
|
||||
const topLevelBucket = _.assign({}, esResponse.aggregations, {
|
||||
doc_count: esResponse.hits.total
|
||||
});
|
||||
|
||||
const topLevelBucket = _.assign({}, esResponse.aggregations, {
|
||||
doc_count: esResponse.hits.total
|
||||
});
|
||||
collectBucket(write, topLevelBucket, '', 1);
|
||||
|
||||
collectBucket(write, topLevelBucket, '', 1);
|
||||
|
||||
return write.response();
|
||||
}
|
||||
|
||||
/**
|
||||
* read an aggregation from a bucket, which is *might* be found at key (if
|
||||
* the response came in object form), and will recurse down the aggregation
|
||||
* tree and will pass the read values to the ResponseWriter.
|
||||
*
|
||||
* @param {object} bucket - a bucket from the aggResponse
|
||||
* @param {undefined|string} key - the key where the bucket was found
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function collectBucket(write, bucket, key, aggScale) {
|
||||
const agg = write.aggStack.shift();
|
||||
const aggInfo = agg.write();
|
||||
aggScale *= aggInfo.metricScale || 1;
|
||||
|
||||
switch (agg.schema.group) {
|
||||
case 'buckets':
|
||||
const buckets = new Buckets(bucket[agg.id], agg.params);
|
||||
if (buckets.length) {
|
||||
const splitting = write.canSplit && agg.schema.name === 'split';
|
||||
if (splitting) {
|
||||
write.split(agg, buckets, function forEachBucket(subBucket, key) {
|
||||
collectBucket(write, subBucket, agg.getKey(subBucket, key), aggScale);
|
||||
});
|
||||
} else {
|
||||
buckets.forEach(function (subBucket, key) {
|
||||
write.cell(agg, agg.getKey(subBucket, key), function () {
|
||||
collectBucket(write, subBucket, agg.getKey(subBucket, key), aggScale);
|
||||
}, subBucket.filters);
|
||||
});
|
||||
}
|
||||
} else if (write.partialRows && write.metricsForAllBuckets && write.minimalColumns) {
|
||||
// we don't have any buckets, but we do have metrics at this
|
||||
// level, then pass all the empty buckets and jump back in for
|
||||
// the metrics.
|
||||
write.aggStack.unshift(agg);
|
||||
passEmptyBuckets(write, bucket, key, aggScale);
|
||||
write.aggStack.shift();
|
||||
} else {
|
||||
// we don't have any buckets, and we don't have isHierarchical
|
||||
// data, so no metrics, just try to write the row
|
||||
write.row();
|
||||
}
|
||||
break;
|
||||
case 'metrics':
|
||||
let value = agg.getValue(bucket);
|
||||
// since the aggregation could be a non integer (such as a max date)
|
||||
// only do the scaling calculation if it is needed.
|
||||
if (aggScale !== 1) {
|
||||
value *= aggScale;
|
||||
}
|
||||
write.cell(agg, value, function () {
|
||||
if (!write.aggStack.length) {
|
||||
// row complete
|
||||
write.row();
|
||||
} else {
|
||||
// process the next agg at this same level
|
||||
collectBucket(write, bucket, key, aggScale);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
write.aggStack.unshift(agg);
|
||||
}
|
||||
|
||||
// write empty values for each bucket agg, then write
|
||||
// the metrics from the initial bucket using collectBucket()
|
||||
function passEmptyBuckets(write, bucket, key, aggScale) {
|
||||
const agg = write.aggStack.shift();
|
||||
|
||||
switch (agg.schema.group) {
|
||||
case 'metrics':
|
||||
// pass control back to collectBucket()
|
||||
write.aggStack.unshift(agg);
|
||||
collectBucket(write, bucket, key, aggScale);
|
||||
return;
|
||||
|
||||
case 'buckets':
|
||||
write.cell(agg, '', function () {
|
||||
passEmptyBuckets(write, bucket, key, aggScale);
|
||||
});
|
||||
}
|
||||
|
||||
write.aggStack.unshift(agg);
|
||||
}
|
||||
|
||||
return notify.timed('tabify agg response', tabifyAggResponse);
|
||||
return write.response();
|
||||
}
|
||||
|
||||
/**
|
||||
* read an aggregation from a bucket, which is *might* be found at key (if
|
||||
* the response came in object form), and will recurse down the aggregation
|
||||
* tree and will pass the read values to the ResponseWriter.
|
||||
*
|
||||
* @param {object} bucket - a bucket from the aggResponse
|
||||
* @param {undefined|string} key - the key where the bucket was found
|
||||
* @returns {undefined}
|
||||
*/
|
||||
function collectBucket(write, bucket, key, aggScale) {
|
||||
const agg = write.aggStack.shift();
|
||||
const aggInfo = agg.write();
|
||||
aggScale *= aggInfo.metricScale || 1;
|
||||
|
||||
switch (agg.schema.group) {
|
||||
case 'buckets':
|
||||
const buckets = new TabifyBuckets(bucket[agg.id], agg.params);
|
||||
if (buckets.length) {
|
||||
const splitting = write.canSplit && agg.schema.name === 'split';
|
||||
if (splitting) {
|
||||
write.split(agg, buckets, function forEachBucket(subBucket, key) {
|
||||
collectBucket(write, subBucket, agg.getKey(subBucket, key), aggScale);
|
||||
});
|
||||
} else {
|
||||
buckets.forEach(function (subBucket, key) {
|
||||
write.cell(agg, agg.getKey(subBucket, key), function () {
|
||||
collectBucket(write, subBucket, agg.getKey(subBucket, key), aggScale);
|
||||
}, subBucket.filters);
|
||||
});
|
||||
}
|
||||
} else if (write.partialRows && write.metricsForAllBuckets && write.minimalColumns) {
|
||||
// we don't have any buckets, but we do have metrics at this
|
||||
// level, then pass all the empty buckets and jump back in for
|
||||
// the metrics.
|
||||
write.aggStack.unshift(agg);
|
||||
passEmptyBuckets(write, bucket, key, aggScale);
|
||||
write.aggStack.shift();
|
||||
} else {
|
||||
// we don't have any buckets, and we don't have isHierarchical
|
||||
// data, so no metrics, just try to write the row
|
||||
write.row();
|
||||
}
|
||||
break;
|
||||
case 'metrics':
|
||||
let value = agg.getValue(bucket);
|
||||
// since the aggregation could be a non integer (such as a max date)
|
||||
// only do the scaling calculation if it is needed.
|
||||
if (aggScale !== 1) {
|
||||
value *= aggScale;
|
||||
}
|
||||
write.cell(agg, value, function () {
|
||||
if (!write.aggStack.length) {
|
||||
// row complete
|
||||
write.row();
|
||||
} else {
|
||||
// process the next agg at this same level
|
||||
collectBucket(write, bucket, key, aggScale);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
write.aggStack.unshift(agg);
|
||||
}
|
||||
|
||||
// write empty values for each bucket agg, then write
|
||||
// the metrics from the initial bucket using collectBucket()
|
||||
function passEmptyBuckets(write, bucket, key, aggScale) {
|
||||
const agg = write.aggStack.shift();
|
||||
|
||||
switch (agg.schema.group) {
|
||||
case 'metrics':
|
||||
// pass control back to collectBucket()
|
||||
write.aggStack.unshift(agg);
|
||||
collectBucket(write, bucket, key, aggScale);
|
||||
return;
|
||||
|
||||
case 'buckets':
|
||||
write.cell(agg, '', function () {
|
||||
passEmptyBuckets(write, bucket, key, aggScale);
|
||||
});
|
||||
}
|
||||
|
||||
write.aggStack.unshift(agg);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,20 +2,18 @@ import $ from 'jquery';
|
|||
import ngMock from 'ng_mock';
|
||||
import expect from 'expect.js';
|
||||
import fixtures from 'fixtures/fake_hierarchical_data';
|
||||
import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify/tabify';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { VisProvider } from 'ui/vis';
|
||||
describe('AggTableGroup Directive', function () {
|
||||
|
||||
let $rootScope;
|
||||
let $compile;
|
||||
let tabifyAggResponse;
|
||||
let Vis;
|
||||
let indexPattern;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function ($injector, Private) {
|
||||
tabifyAggResponse = Private(AggResponseTabifyProvider);
|
||||
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
Vis = Private(VisProvider);
|
||||
|
||||
|
@ -34,7 +32,7 @@ describe('AggTableGroup Directive', function () {
|
|||
|
||||
it('renders a simple split response properly', function () {
|
||||
const vis = new Vis(indexPattern, 'table');
|
||||
$scope.group = tabifyAggResponse(vis, fixtures.metricOnly);
|
||||
$scope.group = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), fixtures.metricOnly);
|
||||
$scope.sort = {
|
||||
columnIndex: null,
|
||||
direction: null
|
||||
|
@ -76,7 +74,7 @@ describe('AggTableGroup Directive', function () {
|
|||
agg.id = 'agg_' + (i + 1);
|
||||
});
|
||||
|
||||
const group = $scope.group = tabifyAggResponse(vis, fixtures.threeTermBuckets);
|
||||
const group = $scope.group = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), fixtures.threeTermBuckets);
|
||||
const $el = $('<kbn-agg-table-group group="group"></kbn-agg-table-group>');
|
||||
$compile($el)($scope);
|
||||
$scope.$digest();
|
||||
|
|
|
@ -5,21 +5,19 @@ import ngMock from 'ng_mock';
|
|||
import expect from 'expect.js';
|
||||
import fixtures from 'fixtures/fake_hierarchical_data';
|
||||
import sinon from 'sinon';
|
||||
import { AggResponseTabifyProvider } from 'ui/agg_response/tabify/tabify';
|
||||
import { tabifyAggResponse } from 'ui/agg_response/tabify/tabify';
|
||||
import FixturesStubbedLogstashIndexPatternProvider from 'fixtures/stubbed_logstash_index_pattern';
|
||||
import { VisProvider } from 'ui/vis';
|
||||
describe('AggTable Directive', function () {
|
||||
|
||||
let $rootScope;
|
||||
let $compile;
|
||||
let tabifyAggResponse;
|
||||
let Vis;
|
||||
let indexPattern;
|
||||
let settings;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function ($injector, Private, config) {
|
||||
tabifyAggResponse = Private(AggResponseTabifyProvider);
|
||||
indexPattern = Private(FixturesStubbedLogstashIndexPatternProvider);
|
||||
Vis = Private(VisProvider);
|
||||
settings = config;
|
||||
|
@ -39,7 +37,11 @@ describe('AggTable Directive', function () {
|
|||
|
||||
it('renders a simple response properly', function () {
|
||||
const vis = new Vis(indexPattern, 'table');
|
||||
$scope.table = tabifyAggResponse(vis, fixtures.metricOnly, { canSplit: false });
|
||||
$scope.table = tabifyAggResponse(
|
||||
vis.getAggConfig().getResponseAggs(),
|
||||
fixtures.metricOnly,
|
||||
{ canSplit: false, hierarchical: vis.isHierarchical() }
|
||||
);
|
||||
|
||||
const $el = $compile('<kbn-agg-table table="table"></kbn-agg-table>')($scope);
|
||||
$scope.$digest();
|
||||
|
@ -71,7 +73,10 @@ describe('AggTable Directive', function () {
|
|||
agg.id = 'agg_' + (i + 1);
|
||||
});
|
||||
|
||||
$scope.table = tabifyAggResponse(vis, fixtures.threeTermBuckets, { canSplit: false });
|
||||
$scope.table = tabifyAggResponse(vis.getAggConfig().getResponseAggs(), fixtures.threeTermBuckets, {
|
||||
canSplit: false,
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
const $el = $('<kbn-agg-table table="table"></kbn-agg-table>');
|
||||
$compile($el)($scope);
|
||||
$scope.$digest();
|
||||
|
@ -134,7 +139,7 @@ describe('AggTable Directive', function () {
|
|||
const oldTimezoneSetting = settings.get('dateFormat:tz');
|
||||
settings.set('dateFormat:tz', 'UTC');
|
||||
|
||||
$scope.table = tabifyAggResponse(vis,
|
||||
$scope.table = tabifyAggResponse(vis.getAggConfig().getResponseAggs(),
|
||||
fixtures.oneTermOneHistogramBucketWithTwoMetricsOneTopHitOneDerivative,
|
||||
{ canSplit: false, minimalColumns: true, asAggConfigResults: true }
|
||||
);
|
||||
|
|
|
@ -2,18 +2,16 @@ import _ from 'lodash';
|
|||
import ngMock from 'ng_mock';
|
||||
import expect from 'expect.js';
|
||||
import sinon from 'sinon';
|
||||
import { AggResponseTabifyTableProvider } from 'ui/agg_response/tabify/_table';
|
||||
import { TabifyTable } from 'ui/agg_response/tabify/_table';
|
||||
import { AggResponseIndexProvider } from 'ui/agg_response/index';
|
||||
import { BasicResponseHandlerProvider } from 'ui/vis/response_handlers/basic';
|
||||
|
||||
describe('renderbot#buildChartData', function () {
|
||||
let buildChartData;
|
||||
let aggResponse;
|
||||
let Table;
|
||||
|
||||
beforeEach(ngMock.module('kibana'));
|
||||
beforeEach(ngMock.inject(function (Private) {
|
||||
Table = Private(AggResponseTabifyTableProvider);
|
||||
aggResponse = Private(AggResponseIndexProvider);
|
||||
buildChartData = Private(BasicResponseHandlerProvider).handler;
|
||||
}));
|
||||
|
@ -62,7 +60,7 @@ describe('renderbot#buildChartData', function () {
|
|||
}
|
||||
};
|
||||
const esResp = { hits: { total: 1 } };
|
||||
const tabbed = { tables: [ new Table() ] };
|
||||
const tabbed = { tables: [ new TabifyTable() ] };
|
||||
|
||||
sinon.stub(aggResponse, 'tabify').returns(tabbed);
|
||||
expect(buildChartData.call(renderbot, esResp)).to.eql(chart);
|
||||
|
@ -71,7 +69,7 @@ describe('renderbot#buildChartData', function () {
|
|||
it('converts table groups into rows/columns wrappers for charts', function () {
|
||||
const converter = sinon.stub().returns('chart');
|
||||
const esResp = { hits: { total: 1 } };
|
||||
const tables = [new Table(), new Table(), new Table(), new Table()];
|
||||
const tables = [new TabifyTable(), new TabifyTable(), new TabifyTable(), new TabifyTable()];
|
||||
|
||||
const renderbot = {
|
||||
vis: {
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
import { AggResponseIndexProvider } from 'ui/agg_response/index';
|
||||
import { AggResponseTabifyTableProvider } from 'ui/agg_response/tabify/_table';
|
||||
import { TabifyTable } from 'ui/agg_response/tabify/_table';
|
||||
|
||||
import { VisResponseHandlersRegistryProvider } from 'ui/registry/vis_response_handlers';
|
||||
|
||||
const BasicResponseHandlerProvider = function (Private) {
|
||||
const aggResponse = Private(AggResponseIndexProvider);
|
||||
const Table = Private(AggResponseTabifyTableProvider);
|
||||
|
||||
function convertTableGroup(vis, tableGroup) {
|
||||
const tables = tableGroup.tables;
|
||||
const firstChild = tables[0];
|
||||
if (firstChild instanceof Table) {
|
||||
if (firstChild instanceof TabifyTable) {
|
||||
|
||||
const chart = convertTable(vis, firstChild);
|
||||
// if chart is within a split, assign group title to its label
|
||||
|
@ -53,9 +52,10 @@ const BasicResponseHandlerProvider = function (Private) {
|
|||
resolve(aggResponse.hierarchical(vis, response));
|
||||
}
|
||||
|
||||
const tableGroup = aggResponse.tabify(vis, response, {
|
||||
const tableGroup = aggResponse.tabify(vis.getAggConfig().getResponseAggs(), response, {
|
||||
canSplit: true,
|
||||
asAggConfigResults: true
|
||||
asAggConfigResults: true,
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
let converted = convertTableGroup(vis, tableGroup);
|
||||
|
|
|
@ -10,9 +10,10 @@ const TabifyResponseHandlerProvider = function (Private) {
|
|||
handler: function (vis, response) {
|
||||
return new Promise((resolve) => {
|
||||
|
||||
const tableGroup = aggResponse.tabify(vis, response, {
|
||||
const tableGroup = aggResponse.tabify(vis.getAggConfig().getResponseAggs(), response, {
|
||||
canSplit: true,
|
||||
asAggConfigResults: _.get(vis, 'type.responseHandlerConfig.asAggConfigResults', false)
|
||||
asAggConfigResults: _.get(vis, 'type.responseHandlerConfig.asAggConfigResults', false),
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
|
||||
resolve(tableGroup);
|
||||
|
|
|
@ -46,7 +46,9 @@ describe('visualization_editor directive', function () {
|
|||
vis.aggs.forEach(function (agg, i) { agg.id = 'agg_' + (i + 1); });
|
||||
|
||||
$rootScope.vis = vis;
|
||||
$rootScope.visData = aggResponse.tabify(vis, esResponse);
|
||||
$rootScope.visData = aggResponse.tabify(vis.getAggConfig().getResponseAggs(), esResponse, {
|
||||
isHierarchical: vis.isHierarchical()
|
||||
});
|
||||
$rootScope.uiState = require('fixtures/mock_ui_state');
|
||||
$rootScope.searchSource = searchSource;
|
||||
$el = $('<visualization-editor vis="vis" vis-data="visData" ui-state="uiState" search-source="searchSource">');
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue