mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* Initial visualize loader functional tests * Extend plugin test README * Add temporary tz work around * Switch to Australia/North timezone * Add filtering tests * Add all tests * Remove unneeded uiExports * Improve explanation * Add saved object test, add retry
This commit is contained in:
parent
c9f753155e
commit
9f37b1fe51
15 changed files with 996 additions and 0 deletions
|
@ -62,6 +62,11 @@ class VisualizeLoader {
|
|||
* In most of the cases you will need this method, since it allows you to specify
|
||||
* filters, handlers, queries, etc. on the savedObject before rendering.
|
||||
*
|
||||
* We do not encourage you to use this method, since it will most likely be changed
|
||||
* or removed in a future version of Kibana. Rather embed a visualization by its id
|
||||
* via the {@link #embedVisualizationWithId} method.
|
||||
*
|
||||
* @deprecated You should rather embed by id, since this method will be removed in the future.
|
||||
* @param element The DOM element to render the visualization into.
|
||||
* You can alternatively pass a jQuery element instead.
|
||||
* @param savedObj The savedObject as it could be retrieved by the
|
||||
|
@ -106,6 +111,8 @@ class VisualizeLoader {
|
|||
element.className = 'visualize';
|
||||
element.setAttribute('data-test-subj', 'visualizationLoader');
|
||||
container.appendChild(element);
|
||||
// We need the container to have display: flex so visualization will render correctly
|
||||
container.style.display = 'flex';
|
||||
|
||||
// If params specified cssClass, we will set this to the element.
|
||||
if (params.cssClass) {
|
||||
|
|
|
@ -53,6 +53,7 @@ import {
|
|||
ComboBoxProvider,
|
||||
EmbeddingProvider,
|
||||
RenderableProvider,
|
||||
TableProvider,
|
||||
} from './services';
|
||||
|
||||
export default async function ({ readConfigFile }) {
|
||||
|
@ -111,6 +112,7 @@ export default async function ({ readConfigFile }) {
|
|||
comboBox: ComboBoxProvider,
|
||||
embedding: EmbeddingProvider,
|
||||
renderable: RenderableProvider,
|
||||
table: TableProvider,
|
||||
},
|
||||
servers: commonConfig.get('servers'),
|
||||
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,246 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"number_of_shards": "1",
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"doc": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"optionsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"panelsJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"refreshInterval": {
|
||||
"properties": {
|
||||
"display": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"pause": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"section": {
|
||||
"type": "integer"
|
||||
},
|
||||
"value": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeFrom": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timeRestore": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"timeTo": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion_chart_height": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_columns": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_other_interval": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"timelion_rows": {
|
||||
"type": "integer"
|
||||
},
|
||||
"timelion_sheet": {
|
||||
"type": "text"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -30,5 +30,6 @@ export { FlyoutProvider } from './flyout';
|
|||
export { EmbeddingProvider } from './embedding';
|
||||
export { ComboBoxProvider } from './combo_box';
|
||||
export { RenderableProvider } from './renderable';
|
||||
export { TableProvider } from './table';
|
||||
|
||||
export * from './dashboard';
|
||||
|
|
39
test/functional/services/table.js
Normal file
39
test/functional/services/table.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export function TableProvider({ getService }) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
// const retry = getService('retry');
|
||||
|
||||
class Table {
|
||||
|
||||
async getDataFromTestSubj(testSubj) {
|
||||
const table = await testSubjects.find(testSubj);
|
||||
// Convert the data into a nested array format:
|
||||
// [ [cell1_in_row1, cell2_in_row1], [cell1_in_row2, cell2_in_row2] ]
|
||||
const rows = await table.findAllByTagName('tr');
|
||||
return await Promise.all(rows.map(async row => {
|
||||
const cells = await row.findAllByTagName('td');
|
||||
return await Promise.all(cells.map(async cell => cell.getVisibleText()));
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return new Table();
|
||||
}
|
|
@ -21,3 +21,12 @@ node scripts/functional_tests_server.js --config test/plugin_functional/config.j
|
|||
# Start a test run
|
||||
node scripts/functional_test_runner.js --config test/plugin_functional/config.js
|
||||
```
|
||||
|
||||
## Run Kibana with a test plugin
|
||||
|
||||
In case you want to start Kibana with one of the test plugins (e.g. for developing the
|
||||
test plugin), you can just run:
|
||||
|
||||
```
|
||||
yarn start --plugin-path=test/plugin_functional/plugins/<plugin_folder>
|
||||
```
|
|
@ -30,6 +30,7 @@ export default async function ({ readConfigFile }) {
|
|||
return {
|
||||
testFiles: [
|
||||
require.resolve('./test_suites/app_plugins'),
|
||||
require.resolve('./test_suites/embedding_visualizations'),
|
||||
require.resolve('./test_suites/panel_actions'),
|
||||
],
|
||||
services: functionalConfig.get('services'),
|
||||
|
|
39
test/plugin_functional/plugins/visualize_embedding/index.js
Normal file
39
test/plugin_functional/plugins/visualize_embedding/index.js
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default function (kibana) {
|
||||
return new kibana.Plugin({
|
||||
uiExports: {
|
||||
app: {
|
||||
title: 'Embedding Vis',
|
||||
description: 'This is a sample plugin to test embedding of visualizations',
|
||||
main: 'plugins/visualize_embedding/app',
|
||||
}
|
||||
},
|
||||
|
||||
init(server) {
|
||||
// The following lines copy over some configuration variables from Kibana
|
||||
// to this plugin. This will be needed when embedding visualizations, so that e.g.
|
||||
// region map is able to get its configuration.
|
||||
server.injectUiAppVars('visualize_embedding', async () => {
|
||||
return await server.getInjectedUiAppVars('kibana');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"name": "visualize_embedding",
|
||||
"version": "kibana"
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
// This is required so some default styles and required scripts/Angular modules are loaded,
|
||||
// or the timezone setting is correctly applied.
|
||||
import 'ui/autoload/all';
|
||||
|
||||
// These are all the required uiExports you need to import in case you want to embed visualizations.
|
||||
import 'uiExports/visTypes';
|
||||
import 'uiExports/visResponseHandlers';
|
||||
import 'uiExports/visRequestHandlers';
|
||||
import 'uiExports/visEditorTypes';
|
||||
import 'uiExports/visualize';
|
||||
import 'uiExports/savedObjectTypes';
|
||||
import 'uiExports/fieldFormats';
|
||||
import 'uiExports/search';
|
||||
|
||||
// ----------- TODO Remove once https://github.com/elastic/kibana/pull/22623 is merged
|
||||
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
function setDefaultTimezone(tz) {
|
||||
moment.tz.setDefault(tz);
|
||||
}
|
||||
|
||||
function setStartDayOfWeek(day) {
|
||||
const dow = moment.weekdays().indexOf(day);
|
||||
moment.updateLocale(moment.locale(), { week: { dow } });
|
||||
}
|
||||
|
||||
const uiSettings = chrome.getUiSettingsClient();
|
||||
|
||||
setDefaultTimezone(uiSettings.get('dateFormat:tz'));
|
||||
setStartDayOfWeek(uiSettings.get('dateFormat:dow'));
|
||||
|
||||
uiSettings.subscribe(({ key, newValue }) => {
|
||||
if (key === 'dateFormat:tz') {
|
||||
setDefaultTimezone(newValue);
|
||||
} else if (key === 'dateFormat:dow') {
|
||||
setStartDayOfWeek(newValue);
|
||||
}
|
||||
});
|
||||
|
||||
// ----------------- END OF REMOVAL ----------
|
||||
|
||||
import { Main } from './components/main';
|
||||
|
||||
const app = uiModules.get('apps/firewallDemoPlugin', ['kibana']);
|
||||
|
||||
app.config($locationProvider => {
|
||||
$locationProvider.html5Mode({
|
||||
enabled: false,
|
||||
requireBase: false,
|
||||
rewriteLinks: false,
|
||||
});
|
||||
});
|
||||
app.config(stateManagementConfigProvider =>
|
||||
stateManagementConfigProvider.disable()
|
||||
);
|
||||
|
||||
function RootController($scope, $element) {
|
||||
const domNode = $element[0];
|
||||
|
||||
// render react to DOM
|
||||
render(<Main />, domNode);
|
||||
|
||||
// unmount react on controller destroy
|
||||
$scope.$on('$destroy', () => {
|
||||
unmountComponentAtNode(domNode);
|
||||
});
|
||||
}
|
||||
|
||||
chrome.setRootController('firewallDemoPlugin', RootController);
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* 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 React from 'react';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFormRow,
|
||||
EuiLoadingChart,
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiPageContentBody,
|
||||
EuiPageContentHeader,
|
||||
EuiSelect,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { embeddingSamples } from '../embedding';
|
||||
|
||||
const VISUALIZATION_OPTIONS = [
|
||||
{ value: '', text: '' },
|
||||
{ value: 'timebased', text: 'Time based' },
|
||||
{ value: 'timebased_with-filters', text: 'Time based (with filters)' },
|
||||
{ value: 'timebased_no-datehistogram', text: 'Time based data without date histogram' }
|
||||
];
|
||||
|
||||
class Main extends React.Component {
|
||||
|
||||
chartDiv = React.createRef();
|
||||
state = {
|
||||
loading: false,
|
||||
selectedParams: null,
|
||||
selectedVis: null,
|
||||
};
|
||||
|
||||
embedVisualization = async () => {
|
||||
if (this.handler) {
|
||||
// Whenever a visualization is about to be removed from DOM that you embedded,
|
||||
// you need to call `destroy` on the handler to make sure the visualization is
|
||||
// teared down correctly.
|
||||
this.handler.destroy();
|
||||
this.chartDiv.current.innerHTML = '';
|
||||
}
|
||||
|
||||
const { selectedParams, selectedVis } = this.state;
|
||||
if (selectedParams && selectedVis) {
|
||||
this.setState({ loading: true });
|
||||
const sample = embeddingSamples.find(el => el.id === selectedParams);
|
||||
this.handler = await sample.run(this.chartDiv.current, selectedVis);
|
||||
// handler.whenFirstRenderComplete() will return a promise that resolves once the first
|
||||
// rendering after embedding has finished.
|
||||
await this.handler.whenFirstRenderComplete();
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
}
|
||||
|
||||
onChangeVisualization = async (ev) => {
|
||||
this.setState({
|
||||
selectedVis: ev.target.value,
|
||||
}, this.embedVisualization);
|
||||
};
|
||||
|
||||
onSelectSample = async (ev) => {
|
||||
this.setState({
|
||||
selectedParams: ev.target.value,
|
||||
}, this.embedVisualization);
|
||||
};
|
||||
|
||||
render() {
|
||||
const samples = [
|
||||
{ value: '', text: '' },
|
||||
...embeddingSamples.map(({ id, title }) => ({
|
||||
value: id,
|
||||
text: title,
|
||||
}))
|
||||
];
|
||||
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPageContent>
|
||||
<EuiPageContentHeader>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow label="Select visualizations">
|
||||
<EuiSelect
|
||||
data-test-subj="visSelect"
|
||||
options={VISUALIZATION_OPTIONS}
|
||||
onChange={this.onChangeVisualization}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow label="Select embedding parameters">
|
||||
<EuiSelect
|
||||
data-test-subj="embeddingParamsSelect"
|
||||
options={samples}
|
||||
onChange={this.onSelectSample}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
{ this.state.loading &&
|
||||
<EuiFlexItem>
|
||||
<EuiLoadingChart size="xl" data-test-subj="visLoadingIndicator" />
|
||||
</EuiFlexItem>
|
||||
}
|
||||
</EuiFlexGroup>
|
||||
</EuiPageContentHeader>
|
||||
<EuiPageContentBody>
|
||||
{/*
|
||||
The element you want to render into should have its dimension set (via a fixed height, flexbox, absolute positioning, etc.),
|
||||
since the visualization will render with exactly the size of that element, i.e. the container size determines the
|
||||
visualization size.
|
||||
*/}
|
||||
<div ref={this.chartDiv} style={{ height: '500px' }} />
|
||||
</EuiPageContentBody>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { Main };
|
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This files shows a couple of examples how to use the visualize loader API
|
||||
* to embed visualizations.
|
||||
*/
|
||||
|
||||
import { getVisualizeLoader } from 'ui/visualize';
|
||||
import chrome from 'ui/chrome';
|
||||
|
||||
export const embeddingSamples = [
|
||||
|
||||
{
|
||||
id: 'none',
|
||||
title: 'No parameters',
|
||||
async run(domNode, id) {
|
||||
// You always need to retrieve the visualize loader for embedding visualizations.
|
||||
const loader = await getVisualizeLoader();
|
||||
// Use the embedVisualizationWithId method to embed a visualization by its id. The id is the id of the
|
||||
// saved object in the .kibana index (you can find the id via Management -> Saved Objects).
|
||||
//
|
||||
// Pass in a DOM node that you want to embed that visualization into. Note: the loader will
|
||||
// use the size of that DOM node.
|
||||
//
|
||||
// The call will return a handler for the visualization with methods to interact with it.
|
||||
// Check the components/main.js file to see how this handler is used. Most important: you need to call
|
||||
// `destroy` on the handler once you are about to remove the visualization from the DOM.
|
||||
//
|
||||
// Note: If the visualization you want to embed contains date histograms with an auto interval, you need
|
||||
// to specify the timeRange parameter (see below).
|
||||
return loader.embedVisualizationWithId(domNode, id, {});
|
||||
}
|
||||
}, {
|
||||
id: 'timerange',
|
||||
title: 'timeRange',
|
||||
async run(domNode, id) {
|
||||
const loader = await getVisualizeLoader();
|
||||
// If you want to filter down the data to a specific time range, you can specify a
|
||||
// timeRange in the parameters to the embedding call.
|
||||
// You can either use an absolute time range as seen below. You can also specify
|
||||
// a datemath string, like "now-7d", "now-1w/w" for the from or to key.
|
||||
// You can also directly assign a moment JS or regular JavaScript Date object.
|
||||
return loader.embedVisualizationWithId(domNode, id, {
|
||||
timeRange: {
|
||||
from: '2015-09-20 20:00:00.000',
|
||||
to: '2015-09-21 20:00:00.000',
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
id: 'query',
|
||||
title: 'query',
|
||||
async run(domNode, id) {
|
||||
const loader = await getVisualizeLoader();
|
||||
// You can specify a query that should filter down the data via the query parameter.
|
||||
// It must have a language key which must be one of the supported query languages of Kibana,
|
||||
// which are at the moment: 'lucene' or 'kquery'.
|
||||
// The query key must then hold the actual query in the specified language for filtering.
|
||||
return loader.embedVisualizationWithId(domNode, id, {
|
||||
query: {
|
||||
language: 'lucene',
|
||||
query: 'extension.raw:jpg',
|
||||
}
|
||||
});
|
||||
}
|
||||
}, {
|
||||
id: 'filters',
|
||||
title: 'filters',
|
||||
async run(domNode, id) {
|
||||
const loader = await getVisualizeLoader();
|
||||
// You can specify an array of filters that should apply to the query.
|
||||
// The format of a filter must match the format the filter bar is using internally.
|
||||
// This has a query key, which holds the query part of an Elasticsearch query
|
||||
// and a meta key allowing to set some meta values, most important for this API
|
||||
// the `negate` option to negate the filter.
|
||||
return loader.embedVisualizationWithId(domNode, id, {
|
||||
filters: [
|
||||
{
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{ match_phrase: { 'extension.raw': 'jpg' } },
|
||||
{ match_phrase: { 'extension.raw': 'png' } },
|
||||
]
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
negate: true
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}, {
|
||||
id: 'filters_query_timerange',
|
||||
title: 'filters & query & timeRange',
|
||||
async run(domNode, id) {
|
||||
const loader = await getVisualizeLoader();
|
||||
// You an of course combine timeRange, query and filters options all together
|
||||
// to filter the data in the embedded visualization.
|
||||
return loader.embedVisualizationWithId(domNode, id, {
|
||||
timeRange: {
|
||||
from: '2015-09-20 20:00:00.000',
|
||||
to: '2015-09-21 20:00:00.000',
|
||||
},
|
||||
query: {
|
||||
language: 'lucene',
|
||||
query: 'bytes:>2000'
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{ match_phrase: { 'extension.raw': 'jpg' } },
|
||||
{ match_phrase: { 'extension.raw': 'png' } },
|
||||
]
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
negate: true
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}, {
|
||||
id: 'savedobject_filter_query_timerange',
|
||||
title: 'filters & query & time (use saved object)',
|
||||
async run(domNode, id) {
|
||||
const loader = await getVisualizeLoader();
|
||||
// Besides embedding via the id of the visualizataion, the API offers the possibility to
|
||||
// embed via the saved visualization object.
|
||||
//
|
||||
// WE ADVISE YOU NOT TO USE THIS INSIDE ANY PLUGIN!
|
||||
//
|
||||
// Since the format of the saved visualization object will change in the future and because
|
||||
// this still requires you to talk to old Angular code, we do not encourage you to use this
|
||||
// way of embedding in any plugin. It's likely it will be removed or changed in a future version.
|
||||
const $injector = await chrome.dangerouslyGetActiveInjector();
|
||||
const savedVisualizations = $injector.get('savedVisualizations');
|
||||
const savedVis = await savedVisualizations.get(id);
|
||||
return loader.embedVisualizationWithSavedObject(domNode, savedVis, {
|
||||
timeRange: {
|
||||
from: '2015-09-20 20:00:00.000',
|
||||
to: '2015-09-21 20:00:00.000',
|
||||
},
|
||||
query: {
|
||||
language: 'lucene',
|
||||
query: 'bytes:>2000'
|
||||
},
|
||||
filters: [
|
||||
{
|
||||
query: {
|
||||
bool: {
|
||||
should: [
|
||||
{ match_phrase: { 'extension.raw': 'jpg' } },
|
||||
{ match_phrase: { 'extension.raw': 'png' } },
|
||||
]
|
||||
}
|
||||
},
|
||||
meta: {
|
||||
negate: true
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
];
|
|
@ -0,0 +1,188 @@
|
|||
/*
|
||||
* 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 expect from 'expect.js';
|
||||
|
||||
export default function ({ getService }) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const find = getService('find');
|
||||
const table = getService('table');
|
||||
const retry = getService('retry');
|
||||
|
||||
async function selectVis(id) {
|
||||
await testSubjects.click('visSelect');
|
||||
await find.clickByCssSelector(`option[value="${id}"]`);
|
||||
}
|
||||
|
||||
async function selectParams(id) {
|
||||
await testSubjects.click('embeddingParamsSelect');
|
||||
await find.clickByCssSelector(`option[value="${id}"]`);
|
||||
await retry.try(async () => {
|
||||
await testSubjects.waitForDeleted('visLoadingIndicator');
|
||||
});
|
||||
}
|
||||
|
||||
async function getTableData() {
|
||||
const data = await table.getDataFromTestSubj('paginated-table-body');
|
||||
// Strip away empty rows (at the bottom)
|
||||
return data.filter(row => !row.every(cell => !cell.trim()));
|
||||
}
|
||||
|
||||
describe('embed by id', function describeIndexTests() {
|
||||
|
||||
describe('vis on timebased data without date histogram', () => {
|
||||
before(async () => {
|
||||
await selectVis('timebased_no-datehistogram');
|
||||
});
|
||||
|
||||
it('should correctly embed', async () => {
|
||||
await selectParams('none');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['jpg', '9,109'],
|
||||
['css', '2,159'],
|
||||
['png', '1,373'],
|
||||
['gif', '918'],
|
||||
['php', '445'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly embed specifying a timeRange', async () => {
|
||||
await selectParams('timerange');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['jpg', '3,005'],
|
||||
['css', '720'],
|
||||
['png', '455'],
|
||||
['gif', '300'],
|
||||
['php', '142'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly embed specifying a query', async () => {
|
||||
await selectParams('query');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['jpg', '9,109'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly embed specifying filters', async () => {
|
||||
await selectParams('filters');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['css', '2,159'],
|
||||
['gif', '918'],
|
||||
['php', '445'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly embed specifying filters and query and timeRange', async () => {
|
||||
await selectParams('filters_query_timerange');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['css', '678'],
|
||||
['php', '110'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('vis on timebased data with date histogram with interval auto', () => {
|
||||
before(async () => {
|
||||
await selectVis('timebased');
|
||||
});
|
||||
|
||||
it('should correctly embed specifying a timeRange', async () => {
|
||||
await selectParams('timerange');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['2015-09-20 20:00', '45.159KB', '5.65KB'],
|
||||
['2015-09-21 00:00', '42.428KB', '5.345KB'],
|
||||
['2015-09-21 04:00', '43.717KB', '5.35KB'],
|
||||
['2015-09-21 08:00', '43.228KB', '5.538KB'],
|
||||
['2015-09-21 12:00', '42.83KB', '5.669KB'],
|
||||
['2015-09-21 16:00', '44.908KB', '5.673KB'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly embed specifying filters and query and timeRange', async () => {
|
||||
await selectParams('filters_query_timerange');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['2015-09-20 20:00', '45.391KB', '5.692KB'],
|
||||
['2015-09-21 00:00', '46.57KB', '5.953KB'],
|
||||
['2015-09-21 04:00', '47.339KB', '6.636KB'],
|
||||
['2015-09-21 08:00', '40.5KB', '6.133KB'],
|
||||
['2015-09-21 12:00', '41.31KB', '5.84KB'],
|
||||
['2015-09-21 16:00', '48.012KB', '6.003KB'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('vis on timebased data with date histogram with interval auto and saved filters', () => {
|
||||
before(async () => {
|
||||
await selectVis('timebased_with-filters');
|
||||
});
|
||||
|
||||
it('should correctly embed specifying a timeRange', async () => {
|
||||
await selectParams('timerange');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['2015-09-20 20:00', '21.221KB', '2.66KB'],
|
||||
['2015-09-21 00:00', '22.054KB', '2.63KB'],
|
||||
['2015-09-21 04:00', '15.592KB', '2.547KB'],
|
||||
['2015-09-21 08:00', '4.656KB', '2.328KB'],
|
||||
['2015-09-21 12:00', '17.887KB', '2.694KB'],
|
||||
['2015-09-21 16:00', '20.533KB', '2.529KB'],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should correctly embed specifying filters and query and timeRange', async () => {
|
||||
await selectParams('filters_query_timerange');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['2015-09-20 20:00', '24.567KB', '3.498KB'],
|
||||
['2015-09-21 00:00', '25.984KB', '3.589KB'],
|
||||
['2015-09-21 04:00', '2.543KB', '2.543KB'],
|
||||
['2015-09-21 12:00', '5.783KB', '2.927KB'],
|
||||
['2015-09-21 16:00', '21.107KB', '3.44KB'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('vis visa saved object on timebased data with date histogram with interval auto and saved filters', () => {
|
||||
before(async () => {
|
||||
await selectVis('timebased_with-filters');
|
||||
});
|
||||
|
||||
it('should correctly embed specifying filters and query and timeRange', async () => {
|
||||
await selectParams('savedobject_filter_query_timerange');
|
||||
const data = await getTableData();
|
||||
expect(data).to.be.eql([
|
||||
['2015-09-20 20:00', '24.567KB', '3.498KB'],
|
||||
['2015-09-21 00:00', '25.984KB', '3.589KB'],
|
||||
['2015-09-21 04:00', '2.543KB', '2.543KB'],
|
||||
['2015-09-21 12:00', '5.783KB', '2.927KB'],
|
||||
['2015-09-21 16:00', '21.107KB', '3.44KB'],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default function ({ getService, getPageObjects, loadTestFile }) {
|
||||
const remote = getService('remote');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
const PageObjects = getPageObjects(['common', 'header']);
|
||||
|
||||
describe('embedding visualizations', function () {
|
||||
before(async () => {
|
||||
await esArchiver.loadIfNeeded('../functional/fixtures/es_archiver/logstash_functional');
|
||||
await esArchiver.load('../functional/fixtures/es_archiver/visualize_embedding');
|
||||
await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'Australia/North', 'defaultIndex': 'logstash-*' });
|
||||
await remote.setWindowSize(1300, 900);
|
||||
await PageObjects.common.navigateToApp('settings');
|
||||
await PageObjects.header.clickGlobalNavigationLink('Embedding Vis');
|
||||
});
|
||||
|
||||
loadTestFile(require.resolve('./embed_by_id'));
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue