[Management] Allows for imports to select existing index (#14137) (#14321)

This commit is contained in:
Chris Roberson 2017-10-05 02:06:56 -04:00 committed by Tyler Smalley
parent cbe69ab156
commit 0a0ca43fc4
14 changed files with 763 additions and 58 deletions

View file

@ -11,6 +11,7 @@
<div class="kuiBarSection">
<button
class="kuiButton kuiButton--basic kuiButton--iconText"
data-test-subj="exportAllObjects"
ng-click="exportAll()"
>
<span class="kuiButton__inner">
@ -49,6 +50,7 @@
ng-class="{ 'kuiTab-isSelected': state.tab === service.title }"
ng-repeat="service in services"
ng-click="changeTab(service)"
data-test-subj="objectsTab-{{ service.title }}"
>
{{ service.title }}
<small aria-label="{{:: service.data.length + ' of ' + service.total + ' ' + service.title }}">
@ -126,7 +128,7 @@
</div>
<!-- Table -->
<table class="kuiTable" ng-if="service.data.length">
<table class="kuiTable" ng-if="service.data.length" data-test-subj="objectsTable-{{service.title}}">
<thead>
<tr>
<th class="kuiTableHeaderCell kuiTableHeaderCell--checkBox">
@ -148,6 +150,7 @@
<tr
ng-repeat="item in service.data | orderBy:'title'"
class="kuiTableRow"
data-test-subj="objectsTableRow"
>
<td class="kuiTableRowCell kuiTableRowCell--checkBox">
<div class="kuiTableRowCell__liner">

View file

@ -7,10 +7,25 @@ import 'ui/directives/file_upload';
import uiRoutes from 'ui/routes';
import { SavedObjectsClientProvider } from 'ui/saved_objects';
import { uiModules } from 'ui/modules';
import { showChangeIndexModal } from './show_change_index_modal';
import { SavedObjectNotFound } from 'ui/errors';
const indexPatternsResolutions = {
indexPatterns: function (Private) {
const savedObjectsClient = Private(SavedObjectsClientProvider);
return savedObjectsClient.find({
type: 'index-pattern',
fields: ['title'],
perPage: 10000
}).then(response => response.savedObjects);
}
};
uiRoutes
.when('/management/kibana/objects', {
template: objectIndexHTML
template: objectIndexHTML,
resolve: indexPatternsResolutions
});
uiRoutes
@ -19,7 +34,7 @@ uiRoutes
});
uiModules.get('apps/management')
.directive('kbnManagementObjects', function (kbnIndex, Notifier, Private, kbnUrl, Promise, confirmModal) {
.directive('kbnManagementObjects', function ($route, kbnIndex, Notifier, Private, kbnUrl, Promise, confirmModal) {
const savedObjectsClient = Private(SavedObjectsClientProvider);
return {
@ -188,61 +203,92 @@ uiModules.get('apps/management')
}
);
})
.then((overwriteAll) => {
function importDocument(doc) {
const { service } = find($scope.services, { type: doc._type }) || {};
.then((overwriteAll) => {
const conflictedIndexPatterns = [];
if (!service) {
const msg = `Skipped import of "${doc._source.title}" (${doc._id})`;
const reason = `Invalid type: "${doc._type}"`;
function importDocument(doc) {
const { service } = find($scope.services, { type: doc._type }) || {};
notify.warning(`${msg}, ${reason}`, {
lifetime: 0,
});
if (!service) {
const msg = `Skipped import of "${doc._source.title}" (${doc._id})`;
const reason = `Invalid type: "${doc._type}"`;
return;
notify.warning(`${msg}, ${reason}`, {
lifetime: 0,
});
return;
}
return service.get()
.then(function (obj) {
obj.id = doc._id;
return obj.applyESResp(doc)
.then(() => {
return obj.save({ confirmOverwrite : !overwriteAll });
})
.catch((err) => {
if (err instanceof SavedObjectNotFound && err.savedObjectType === 'index-pattern') {
conflictedIndexPatterns.push({ obj, doc });
return;
}
// swallow errors here so that the remaining promise chain executes
err.message = `Importing ${obj.title} (${obj.id}) failed: ${err.message}`;
notify.error(err);
});
});
}
function groupByType(docs) {
const defaultDocTypes = {
searches: [],
other: [],
};
return docs.reduce((types, doc) => {
switch (doc._type) {
case 'search':
types.searches.push(doc);
break;
default:
types.other.push(doc);
}
return types;
}, defaultDocTypes);
}
return service.get()
.then(function (obj) {
obj.id = doc._id;
return obj.applyESResp(doc)
.then(() => {
return obj.save({ confirmOverwrite : !overwriteAll });
})
.catch((err) => {
// swallow errors here so that the remaining promise chain executes
err.message = `Importing ${obj.title} (${obj.id}) failed: ${err.message}`;
notify.error(err);
});
});
}
const docTypes = groupByType(docs);
function groupByType(docs) {
const defaultDocTypes = {
searches: [],
other: [],
};
return docs.reduce((types, doc) => {
switch (doc._type) {
case 'search':
types.searches.push(doc);
break;
default:
types.other.push(doc);
}
return types;
}, defaultDocTypes);
}
const docTypes = groupByType(docs);
return Promise.map(docTypes.searches, importDocument)
.then(() => Promise.map(docTypes.other, importDocument))
.then(refreshData)
.catch(notify.error);
});
return Promise.map(docTypes.searches, importDocument)
.then(() => Promise.map(docTypes.other, importDocument))
.then(() => {
if (conflictedIndexPatterns.length) {
showChangeIndexModal(
(objs) => {
return Promise.map(
conflictedIndexPatterns,
({ obj }) => {
const oldIndexId = obj.searchSource.getOwn('index');
const newIndexId = objs.find(({ oldId }) => oldId === oldIndexId).newId;
if (newIndexId === oldIndexId) {
// Skip
return;
}
return obj.hydrateIndexPattern(newIndexId)
.then(() => obj.save({ confirmOverwrite : !overwriteAll }));
}
).then(refreshData);
},
conflictedIndexPatterns,
$route.current.locals.indexPatterns,
);
} else {
return refreshData();
}
})
.catch(notify.error);
});
};
// TODO: Migrate all scope methods to the controller.

View file

@ -0,0 +1,215 @@
import React from 'react';
import PropTypes from 'prop-types';
import { groupBy, mapValues, take, get } from 'lodash';
import {
KuiModal,
KuiModalHeader,
KuiModalHeaderTitle,
KuiModalBody,
KuiModalBodyText,
KuiModalFooter,
KuiButton,
KuiModalOverlay,
KuiTable,
KuiTableBody,
KuiTableHeader,
KuiTableHeaderCell,
KuiTableRow,
KuiTableRowCell,
KuiControlledTable,
KuiToolBar,
KuiToolBarSection,
KuiPager,
} from 'ui_framework/components';
import { ESC_KEY_CODE } from 'ui_framework/services';
export class ChangeIndexModal extends React.Component {
constructor(props) {
super(props);
const byId = groupBy(props.conflictedObjects, ({ obj }) => obj.searchSource.getOwn('index'));
this.state = {
page: 0,
perPage: 10,
objects: mapValues(byId, (list, indexPatternId) => {
return {
newIndexPatternId: get(props, 'indices[0].id'),
list: list.map(({ doc }) => {
return {
id: indexPatternId,
type: doc._type,
name: doc._source.title,
};
})
};
})
};
}
changeIndex = () => {
const result = Object.keys(this.state.objects).map(indexPatternId => ({
oldId: indexPatternId,
newId: this.state.objects[indexPatternId].newIndexPatternId,
}));
this.props.onChange(result);
};
onIndexChange = (id, event) => {
event.persist();
this.setState(state => {
return {
objects: {
...state.objects,
[id]: {
...state.objects[id],
newIndexPatternId: event.target.value,
}
}
};
});
};
onKeyDown = (event) => {
if (event.keyCode === ESC_KEY_CODE) {
this.props.onClose();
}
};
render() {
const { page, perPage } = this.state;
const totalIndexPatterns = Object.keys(this.state.objects).length;
const indexPatternIds = Object.keys(this.state.objects).slice(page, page + perPage);
const rows = indexPatternIds.map((indexPatternId, key) => {
const objects = this.state.objects[indexPatternId].list;
const sample = take(objects, 5).map((obj, key) => <span key={key}>{obj.name}<br/></span>);
return (
<KuiTableRow key={key}>
<KuiTableRowCell>
{indexPatternId}
</KuiTableRowCell>
<KuiTableRowCell>
{objects.length}
</KuiTableRowCell>
<KuiTableRowCell>
{sample}
</KuiTableRowCell>
<KuiTableRowCell>
<select
className="kuiSelect kuiSelect--medium"
data-test-subj="managementChangeIndexSelection"
value={this.state.objects[indexPatternId].newIndexPatternId}
onChange={this.onIndexChange.bind(this, indexPatternId)}
>
<option value={indexPatternId}>Skip import</option>
{this.props.indices.map((index, i) => {
return (
<option key={i} value={index.id}>
{index.get('title')}
</option>
);
})}
</select>
</KuiTableRowCell>
</KuiTableRow>
);
});
const TableComponent = () => (
<KuiTable className="kuiVerticalRhythm">
<KuiTableHeader>
<KuiTableHeaderCell width="300">
ID
</KuiTableHeaderCell>
<KuiTableHeaderCell width="50">
Count
</KuiTableHeaderCell>
<KuiTableHeaderCell>
Sample of affected objects
</KuiTableHeaderCell>
<KuiTableHeaderCell width="200">
New index pattern
</KuiTableHeaderCell>
</KuiTableHeader>
<KuiTableBody>
{rows}
</KuiTableBody>
</KuiTable>
);
return (
<KuiModalOverlay>
<KuiModal
data-tests-subj="managementChangeIndexModal"
aria-label="Index does not exist"
className="managementChangeIndexModal"
onKeyDown={this.onKeyDown}
>
<KuiModalHeader>
<KuiModalHeaderTitle>
Index Pattern Conflicts
</KuiModalHeaderTitle>
</KuiModalHeader>
<KuiModalBody>
<KuiModalBodyText>
<p>
The following saved objects use index patterns that do not exist.
Please select the index patterns you&apos;d like re-associated them with.
</p>
</KuiModalBodyText>
{ totalIndexPatterns > perPage
?
<KuiControlledTable>
<KuiToolBar>
<KuiToolBarSection>
<KuiPager
startNumber={page + 1}
hasPreviousPage={page >= 1}
hasNextPage={page < totalIndexPatterns}
endNumber={Math.min(totalIndexPatterns, page + perPage)}
totalItems={totalIndexPatterns}
onNextPage={() => this.setState({ page: page + 1 })}
onPreviousPage={() => this.setState({ page: page - 1 })}
/>
</KuiToolBarSection>
</KuiToolBar>
<TableComponent/>
</KuiControlledTable>
:
<TableComponent/>
}
</KuiModalBody>
<KuiModalFooter>
<KuiButton
buttonType="hollow"
data-test-subj="changeIndexCancelButton"
onClick={this.props.onClose}
>
Cancel
</KuiButton>
<KuiButton
buttonType="primary"
data-test-subj="changeIndexConfirmButton"
onClick={this.changeIndex}
>
Confirm all changes
</KuiButton>
</KuiModalFooter>
</KuiModal>
</KuiModalOverlay>
);
}
}
ChangeIndexModal.propTypes = {
onChange: PropTypes.func,
onClose: PropTypes.func,
conflictedObjects: PropTypes.arrayOf(PropTypes.shape({
obj: PropTypes.object.isRequired,
doc: PropTypes.object.isRequired,
})).isRequired,
indices: PropTypes.array
};

View file

@ -0,0 +1,28 @@
import { ChangeIndexModal } from './change_index_modal';
import React from 'react';
import ReactDOM from 'react-dom';
export function showChangeIndexModal(onChange, conflictedObjects, indices = []) {
const container = document.createElement('div');
const closeModal = () => {
document.body.removeChild(container);
};
const onIndexChangeConfirmed = (newIndex) => {
onChange(newIndex);
closeModal();
};
document.body.appendChild(container);
const element = (
<ChangeIndexModal
onChange={onIndexChangeConfirmed}
onClose={closeModal}
conflictedObjects={conflictedObjects}
indices={indices}
/>
);
ReactDOM.render(element, container);
}

View file

@ -125,6 +125,10 @@ kbn-management-objects {
}
}
.managementChangeIndexModal {
width: 800px;
}
kbn-management-advanced {
// super specific rule to override bootstrap's equally specific rule
// https://github.com/twbs/bootstrap/blob/1f329f8f17aa989eabc6e94bdcab93e69ef0e463/less/tables.less#L35

View file

@ -118,7 +118,7 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
*
* @return {Promise<IndexPattern | null>}
*/
const hydrateIndexPattern = () => {
this.hydrateIndexPattern = (id) => {
if (!this.searchSource) {
return Promise.resolve(null);
}
@ -128,7 +128,7 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
return Promise.resolve(null);
}
let index = config.indexPattern || this.searchSource.getOwn('index');
let index = id || config.indexPattern || this.searchSource.getOwn('index');
if (!index) {
return Promise.resolve(null);
@ -163,7 +163,7 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
if (!this.id) {
// just assign the defaults and be done
_.assign(this, this.defaults);
return hydrateIndexPattern().then(() => {
return this.hydrateIndexPattern().then(() => {
return afterESResp.call(this);
});
}
@ -217,7 +217,7 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
return Promise.try(() => {
parseSearchSource(meta.searchSourceJSON);
return hydrateIndexPattern();
return this.hydrateIndexPattern();
}).then(() => {
return Promise.cast(afterESResp.call(this, resp));
});

View file

@ -0,0 +1,93 @@
import expect from 'expect.js';
import path from 'path';
export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const kibanaServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['common', 'settings']);
describe('import objects', function describeIndexTests() {
before(async function () {
// delete .kibana index and then wait for Kibana to re-create it
await kibanaServer.uiSettings.replace({});
await PageObjects.settings.navigateTo();
await esArchiver.load('management');
});
after(async function () {
await esArchiver.unload('management');
});
it('should import saved objects normally', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects.json'));
await PageObjects.common.clickConfirmOnModal();
await PageObjects.settings.clickVisualizationsTab();
const rowCount = await retry.try(async () => {
const rows = await PageObjects.settings.getVisualizationRows();
if (rows.length !== 2) {
throw 'Not loaded yet';
}
return rows.length;
});
expect(rowCount).to.be(2);
});
it('should import conflicts using a confirm modal', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects-conflicts.json'));
await PageObjects.common.clickConfirmOnModal();
await PageObjects.settings.setImportIndexFieldOption(2);
await PageObjects.settings.clickChangeIndexConfirmButton();
await PageObjects.settings.clickVisualizationsTab();
const rowCount = await retry.try(async () => {
const rows = await PageObjects.settings.getVisualizationRows();
if (rows.length !== 2) {
throw 'Not loaded yet';
}
return rows.length;
});
expect(rowCount).to.be(2);
});
it('should allow for overrides', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects.json'));
await PageObjects.common.clickConfirmOnModal();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects.json'));
await PageObjects.common.clickConfirmOnModal();
await PageObjects.settings.clickVisualizationsTab();
const rowCount = await retry.try(async () => {
const rows = await PageObjects.settings.getVisualizationRows();
if (rows.length !== 2) {
throw 'Not loaded yet';
}
return rows.length;
});
expect(rowCount).to.be(2);
});
it('should allow for cancelling overrides', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects.json'));
await PageObjects.common.clickConfirmOnModal();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects.json'));
await PageObjects.common.clickCancelOnModal(true);
await PageObjects.common.clickConfirmOnModal();
await PageObjects.settings.clickVisualizationsTab();
const rowCount = await retry.try(async () => {
const rows = await PageObjects.settings.getVisualizationRows();
if (rows.length !== 2) {
throw 'Not loaded yet';
}
return rows.length;
});
expect(rowCount).to.be(2);
});
});
}

View file

@ -0,0 +1,19 @@
[
{
"_id": "082f1d60-a2e7-11e7-bb30-233be9be6a15",
"_type": "visualization",
"_source": {
"title": "Log Agents",
"visState": "{\"title\":\"Log Agents\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{\"text\":\"agent.raw: Descending\"}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"agent.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"d1e4c910-a2e6-11e7-bb30-233be9be6a15\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"_meta": {
"savedObjectVersion": 2
}
}
]

View file

@ -0,0 +1,19 @@
[
{
"_id": "082f1d60-a2e7-11e7-bb30-233be9be6a15",
"_type": "visualization",
"_source": {
"title": "Log Agents",
"visState": "{\"title\":\"Log Agents\",\"type\":\"area\",\"params\":{\"type\":\"area\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"truncate\":100},\"title\":{\"text\":\"agent.raw: Descending\"}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":\"true\",\"type\":\"area\",\"mode\":\"stacked\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"drawLinesBetweenPoints\":true,\"showCircles\":true,\"interpolate\":\"linear\",\"valueAxis\":\"ValueAxis-1\"}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"enabled\":true,\"type\":\"terms\",\"schema\":\"segment\",\"params\":{\"field\":\"agent.raw\",\"size\":5,\"order\":\"desc\",\"orderBy\":\"1\"}}]}",
"uiStateJSON": "{}",
"description": "",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"f1e4c910-a2e6-11e7-bb30-233be9be6a15\",\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"_meta": {
"savedObjectVersion": 2
}
}
]

View file

@ -24,6 +24,7 @@ export default function ({ getService, loadTestFile }) {
loadTestFile(require.resolve('./_scripted_fields'));
loadTestFile(require.resolve('./_index_pattern_filter'));
loadTestFile(require.resolve('./_scripted_fields_filter'));
loadTestFile(require.resolve('./_import_objects'));
});
}

View file

@ -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"
}
}
}
}
}
}
}
}

View file

@ -230,10 +230,14 @@ export function CommonPageProvider({ getService, getPageObjects }) {
await remote.pressKeys('\uE007');
}
async clickCancelOnModal() {
// pass in true if your test will show multiple modals
// in succession
async clickCancelOnModal(overlayWillStay = false) {
log.debug('Clicking modal cancel');
await testSubjects.click('confirmModalCancelButton');
await this.ensureModalOverlayHidden();
if (!overlayWillStay) {
await this.ensureModalOverlayHidden();
}
}
async isConfirmModalOpen() {

View file

@ -27,6 +27,10 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
await this.clickLinkText('Advanced Settings');
}
async clickKibanaSavedObjects() {
await this.clickLinkText('Saved Objects');
}
async clickKibanaIndices() {
log.debug('clickKibanaIndices link');
await this.clickLinkText('Index Patterns');
@ -484,6 +488,29 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
log.debug('set scripted field script = ' + script);
await testSubjects.setValue('editorFieldScript', script);
}
async importFile(path) {
log.debug(`importFile(${path})`);
await remote.findById('testfile').type(path);
}
async setImportIndexFieldOption(child) {
await remote.setFindTimeout(defaultFindTimeout)
.findByCssSelector(`select[data-test-subj="managementChangeIndexSelection"] > option:nth-child(${child})`)
.click();
}
async clickChangeIndexConfirmButton() {
await (await testSubjects.find('changeIndexConfirmButton')).click();
}
async clickVisualizationsTab() {
await (await testSubjects.find('objectsTab-visualizations')).click();
}
async getVisualizationRows() {
return await testSubjects.findAll(`objectsTableRow`);
}
}
return new SettingsPage();