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

[PoC] visdata interpreter function #33674

Closed
wants to merge 3 commits into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ import { kibanaTable } from './table';
import { tagcloud } from './tagcloud';
import { vislib } from './vislib';
import { visualization } from './visualization';
import { visdata } from './vis_data';

export const functions = [
clog, esaggs, kibana, kibanaContext, vega, timelionVis, tsvb, kibanaMarkdown, inputControlVis,
metric, kibanaPie, regionmap, tilemap, kibanaTable, tagcloud, vislib, visualization
metric, kibanaPie, regionmap, tilemap, kibanaTable, tagcloud, vislib, visualization, visdata
];
71 changes: 71 additions & 0 deletions src/legacy/core_plugins/interpreter/public/functions/vis_data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { getVisualizeLoader } from 'ui/visualize/loader';

export const visdata = () => ({
name: 'visdata',
type: 'datatable',
help: i18n.translate('interpreter.functions.visualization.help', {
defaultMessage: 'Loads saved visualization data'
}),
context: {
types: ['filter'],
},
args: {
id: {
types: ['string'],
},
},
async fn(context, args) {
const loader = await getVisualizeLoader();
const handler = await loader.loadById(args.id);
if (context.and.find(f => f.type === 'time')) {
const timeFilter = context.and.find(f => f.type === 'time');
handler.dataLoaderParams.timeRange = {
from: timeFilter.from,
to: timeFilter.to,
};
}

let data;

try {
data = await handler.fetch(true, true, false);
} catch (e) {
const negativeTimeIntervalMsg = 'Zero or negative time interval not supported';
if (e.message.includes(negativeTimeIntervalMsg)) {
const errorMessage = `visualization requires time range. prepend expression with
'filters | ' and add time picker to workpad`;
throw new Error(errorMessage);
}
}

if (!data.rows || !data.columns) throw new Error('visualization datasource does not return table');

return {
type: 'datatable',
rows: data.rows,
columns: data.columns.map(column => ({
name: column.id,
Copy link
Member Author

Choose a reason for hiding this comment

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

is there a way to pass title as well ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure what the column object looks like here, but name should be the field name, not some unique identifier used by Kibana.

Also, don't forget to add the column type here, and just use 'string' if you can't detect that (but you should be able to based on the field mappings you have).

})),
};
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { AggConfigs } from '../agg_configs';
import { Vis } from '../vis';

export interface RequestHandlerParams {
tableOnly?: boolean;
searchSource: SearchSource;
aggs: AggConfigs;
timeRange?: TimeRange;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ interface EmbeddedVisualizeHandlerParams extends VisualizeLoaderParams {
Private: IPrivate;
queryFilter: any;
autoFetch?: boolean;
autoRender?: boolean;
}

const RENDER_COMPLETE_EVENT = 'render_complete';
Expand Down Expand Up @@ -201,7 +202,9 @@ export class EmbeddedVisualizeHandler {
this.dataSubject = new Rx.Subject();
this.data$ = this.dataSubject.asObservable().pipe(share());

this.render();
if (params.autoRender !== false) {
this.render();
}
}

/**
Expand Down Expand Up @@ -425,9 +428,14 @@ export class EmbeddedVisualizeHandler {
this.fetchAndRender();
};

private fetch = (forceFetch: boolean = false) => {
private fetch = (
forceFetch: boolean = false,
tableOnly: boolean = false,
handleErrors = true
) => {
this.dataLoaderParams.aggs = this.vis.getAggConfig();
this.dataLoaderParams.forceFetch = forceFetch;
this.dataLoaderParams.tableOnly = tableOnly;
this.dataLoaderParams.inspectorAdapters = this.inspectorAdapters;

this.vis.filters = { timeRange: this.dataLoaderParams.timeRange };
Expand All @@ -450,7 +458,13 @@ export class EmbeddedVisualizeHandler {
}
return data;
})
.catch(this.handleDataLoaderError);
.catch(e => {
if (handleErrors) {
this.handleDataLoaderError(e);
} else {
throw e;
}
});
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ export class VisualizeDataLoader {
filters: filters.concat(savedFilters).filter(f => !f.meta.disabled),
});

if (params.tableOnly) {
return requestHandlerResponse;
}

// No need to call the response handler when there have been no data nor has there been changes
// in the vis-state (response handler does not depend on uiState)
const canSkipResponseHandler =
Expand Down
19 changes: 19 additions & 0 deletions src/legacy/ui/public/visualize/loader/visualize_loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,25 @@ export class VisualizeLoader {
return this.renderVis(el, savedObj, params);
}

public async loadById(savedVisualizationId: string, params: VisualizeLoaderParams) {
return new Promise((resolve, reject) => {
this.savedVisualizations.get(savedVisualizationId).then((savedObj: VisSavedObject) => {
const handlerParams = {
...params,
// lets add query filter angular service to the params
queryFilter: this.Private(FilterBarQueryFilterProvider),
// lets add Private to the params, we'll need to pass it to visualize later
Private: this.Private,
autoRender: false,
};

const element = document.createElement('div');
const handler = new EmbeddedVisualizeHandler(element, savedObj, handlerParams);
resolve(handler);
}, reject);
});
}

/**
* Returns a promise, that resolves to a list of all saved visualizations.
*
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/canvas/public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'uiExports/visEditorTypes';
import 'uiExports/savedObjectTypes';
import 'uiExports/spyModes';
import 'uiExports/fieldFormats';
import 'uiExports/search';

// load application code
import './lib/load_expression_types';
Expand Down