Add API to embed visualizations (#14292) (#14373)

This commit is contained in:
Peter Pisljar 2017-10-10 10:07:44 +02:00 committed by GitHub
parent 72a2af85e9
commit 75dcbf123c
9 changed files with 80 additions and 26 deletions

View file

@ -6,10 +6,12 @@ import { luceneStringToDsl } from '../../../../ui/public/courier/data_source/bui
export function dashboardContextProvider(Private, getAppState) {
return () => {
const appState = getAppState();
const queryFilter = Private(FilterBarQueryFilterProvider);
const bool = { must: [], must_not: [] };
if (!appState) return { bool: bool };
const filterBarFilters = queryFilter.getFilters();
const queryBarQuery = getAppState().query;
const queryBarQuery = appState.query;
if (queryBarQuery.language === 'lucene') {
// Add the query bar filter, its handled differently.

View file

@ -12,16 +12,17 @@ const MetricsRequestHandlerProvider = function (Private, Notifier, config, timef
const timezone = Private(timezoneProvider)();
return new Promise((resolve) => {
const panel = vis.params;
const timeRange = vis.params.timeRange || timefilter.getBounds();
if (panel && panel.id) {
const params = {
timerange: { timezone, ...timefilter.getBounds() },
timerange: { timezone, ...timeRange },
filters: [dashboardContext()],
panels: [panel]
};
try {
const maxBuckets = config.get('metrics:max_buckets');
validateInterval(timefilter, panel, maxBuckets);
validateInterval(timeRange, panel, maxBuckets);
const httpResult = $http.post('../api/metrics/vis/data', params)
.then(resp => resp.data)
.catch(resp => { throw resp.data; });

View file

@ -1,8 +1,8 @@
import { parseInterval } from 'ui/utils/parse_interval';
import { GTE_INTERVAL_RE } from '../../common/interval_regexp';
export function validateInterval(timefilter, panel, maxBuckets) {
export function validateInterval(bounds, panel, maxBuckets) {
const { interval } = panel;
const { min, max } = timefilter.getBounds();
const { min, max } = bounds;
// No need to check auto it will return around 100
if (!interval) return;
if (interval === 'auto') return;

View file

@ -20,6 +20,14 @@ const TimelionRequestHandlerProvider = function (Private, Notifier, $http, $root
const expression = vis.params.expression;
if (!expression) return;
let timeFilter = timefilter.time;
if (vis.params.timeRange) {
timeFilter = {
mode: 'absolute',
from: vis.params.timeRange.min.toJSON(),
to: vis.params.timeRange.max.toJSON()
};
}
const httpResult = $http.post('../api/timelion/run', {
sheet: [expression],
extended: {
@ -27,7 +35,7 @@ const TimelionRequestHandlerProvider = function (Private, Notifier, $http, $root
filter: dashboardContext()
}
},
time: _.extend(timefilter.time, {
time: _.extend(timeFilter, {
interval: vis.params.interval,
timezone: timezone
}),

View file

@ -154,7 +154,7 @@ export function SearchSourceProvider(Promise, Private, config) {
const self = this;
if (self._parent === false) return;
if (self._parent) return self._parent;
return onlyHardLinked ? undefined : Private(RootSearchSourceProvider).get();
return onlyHardLinked || this.skipTimeRangeFilter ? undefined : Private(RootSearchSourceProvider).get();
};
/**

View file

@ -77,7 +77,7 @@ uiModules
$rootScope.$apply();
};
Timefilter.prototype.get = function (indexPattern) {
Timefilter.prototype.get = function (indexPattern, range) {
if (!indexPattern) {
//in CI, we sometimes seem to fail here.
@ -91,8 +91,8 @@ uiModules
const bounds = this.getBounds();
filter = { range : {} };
filter.range[timefield.name] = {
gte: bounds.min.valueOf(),
lte: bounds.max.valueOf(),
gte: range ? range.min.valueOf() : bounds.min.valueOf(),
lte: range ? range.max.valueOf() : bounds.max.valueOf(),
format: 'epoch_millis'
};
}

View file

@ -0,0 +1,36 @@
import $ from 'jquery';
export function VisualizeLoaderProvider($compile, $rootScope, savedVisualizations) {
const renderVis = (el, savedObj, params) => {
const scope = $rootScope.$new();
scope.savedObj = savedObj;
scope.appState = params.appState;
scope.uiState = params.uiState;
scope.timeRange = params.timeRange;
scope.showSpyPanel = params.showSpyPanel;
scope.editorMode = params.editorMode;
const container = el instanceof $ ? el : $(el);
container.html('');
const visEl = $('<visualize saved-obj="savedObj" app-state="appState" ui-state="uiState" ' +
'time-range="timeRange" editor-mode="editorMode" show-spy-panel="showSpyPanel"></visualize>');
const visHtml = $compile(visEl)(scope);
container.html(visHtml);
return visEl;
};
return {
embedVisualizationWithId: async (el, savedVisualizationId, params) => {
return new Promise((resolve) => {
savedVisualizations.get(savedVisualizationId).then(savedObj => {
const element = renderVis(el, savedObj, params);
resolve(element);
});
});
},
embedVisualizationWithSavedObject: (el, savedObj, params) => {
return renderVis(el, savedObj, params);
}
};
}

View file

@ -1,6 +1,6 @@
<visualization-editor
ng-if="editorMode==true"
ng-if="editorMode===true"
vis="vis"
vis-data="visData"
ui-state="uiState"
@ -9,7 +9,7 @@
show-spy-panel="shouldShowSpyPanel()"
/>
<visualization
ng-if="editorMode==false"
ng-if="editorMode!==true"
vis="vis"
vis-data="visData"
ui-state="uiState"

View file

@ -1,4 +1,5 @@
import _ from 'lodash';
import dateMath from '@elastic/datemath';
import { uiModules } from 'ui/modules';
import { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
import visualizeTemplate from 'ui/visualize/visualize.html';
@ -18,7 +19,7 @@ import {
uiModules
.get('kibana/directive', ['ngSanitize'])
.directive('visualize', function (Notifier, Private, timefilter, getAppState, Promise, savedVisualizations) {
.directive('visualize', function (Notifier, Private, timefilter, getAppState, Promise) {
const notify = new Notifier({ location: 'Visualize' });
const requestHandlers = Private(VisRequestHandlersRegistryProvider);
const responseHandlers = Private(VisResponseHandlersRegistryProvider);
@ -36,7 +37,7 @@ uiModules
showSpyPanel: '=?',
editorMode: '=?',
savedObj: '=?',
appState: '=',
appState: '=?',
uiState: '=?',
savedId: '=?',
timeRange: '=?'
@ -45,36 +46,42 @@ uiModules
link: async function ($scope, $el) {
const resizeChecker = new ResizeChecker($el);
if (!$scope.savedObj) {
if (!$scope.savedId) throw(`saved object was not provided to <visualize> directive`);
$scope.savedObj = await savedVisualizations.get($scope.savedId);
}
if (!$scope.savedObj) throw(`saved object was not provided to <visualize> directive`);
if (!$scope.appState) $scope.appState = getAppState();
if (!$scope.uiState) $scope.uiState = new PersistedState({});
$scope.vis = $scope.savedObj.vis;
$scope.editorMode = $scope.editorMode || false;
$scope.vis.editorMode = $scope.editorMode;
$scope.vis.visualizeScope = true;
if ($scope.timeRange) {
$scope.vis.params.timeRange = {
min: dateMath.parse($scope.timeRange.min),
max: dateMath.parse($scope.timeRange.max)
};
$scope.vis.aggs.forEach(agg => {
if (agg.type.name !== 'date_histogram') return;
agg.setTimeRange({
min: new Date($scope.timeRange.min),
max: new Date($scope.timeRange.max)
});
agg.setTimeRange($scope.vis.params.timeRange);
});
const searchSource = $scope.savedObj.searchSource;
const filter = timefilter.get(searchSource.index(), $scope.vis.params.timeRange);
const searchSourceFilters = searchSource.get('filter');
if (searchSourceFilters instanceof Array) {
searchSourceFilters.push(filter);
searchSource.skipTimeRangeFilter = true;
}
}
$scope.editorMode = $scope.editorMode || false;
$scope.vis.editorMode = $scope.editorMode;
// spy panel is supported only with courier request handler
$scope.shouldShowSpyPanel = () => {
if ($scope.vis.type.requestHandler !== 'courier') return false;
return $scope.vis.type.requiresSearch && $scope.showSpyPanel;
};
if (!$scope.appState) $scope.appState = getAppState();
const requestHandler = getHandler(requestHandlers, $scope.vis.type.requestHandler);
const responseHandler = getHandler(responseHandlers, $scope.vis.type.responseHandler);