[Management] Fix importing objects connected to saved searches that contain conflicts (#16004)

* Handle cases of importing objects that are connected to searches, but error out due to the searches index pattern not found

* Add tests and fix flaky ones
This commit is contained in:
Chris Roberson 2018-02-02 14:28:29 -05:00 committed by GitHub
parent b27c5772cf
commit f93b76285b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 136 additions and 89 deletions

View file

@ -31,7 +31,11 @@
ng-repeat="pattern in indexPatternList | orderBy:['-default','title'] track by pattern.id"
class="sidebar-item"
>
<a href="{{::pattern.url}}" class="euiLink euiLink--primary show">
<a
href="{{::pattern.url}}"
class="euiLink euiLink--primary show"
data-test-subj="indexPatternLink"
>
<div class="{{::pattern.class}}">
<i aria-hidden="true" ng-if="pattern.default" class="fa fa-star"></i>
<span ng-bind="::pattern.title"></span>

View file

@ -213,6 +213,11 @@ uiModules.get('apps/management')
// We want to do the same for saved searches, but we want to keep them separate because they need
// to be applied _first_ because other saved objects can be depedent on those saved searches existing
const conflictedSearchDocs = [];
// It's possbile to have saved objects that link to saved searches which then link to index patterns
// and those could error out, but the error comes as an index pattern not found error. We can't resolve
// those the same as way as normal index pattern not found errors, but when those are fixed, it's very
// likely that these saved objects will work once resaved so keep them around to resave them.
const conflictedSavedObjectsLinkedToSavedSearches = [];
function importDocument(swallowErrors, doc) {
const { service } = find($scope.services, { type: doc._type }) || {};
@ -242,7 +247,11 @@ uiModules.get('apps/management')
conflictedSearchDocs.push(doc);
return;
case 'index-pattern':
conflictedIndexPatterns.push({ obj, doc });
if (obj.savedSearchId) {
conflictedSavedObjectsLinkedToSavedSearches.push(obj);
} else {
conflictedIndexPatterns.push({ obj, doc });
}
return;
}
}
@ -280,7 +289,11 @@ uiModules.get('apps/management')
return;
}
return obj.hydrateIndexPattern(newIndexId)
.then(() => obj.save({ confirmOverwrite: !overwriteAll }));
.then(() => saveObject(obj));
}
function saveObject(obj) {
return obj.save({ confirmOverwrite: !overwriteAll });
}
const docTypes = groupByType(docs);
@ -293,6 +306,7 @@ uiModules.get('apps/management')
showChangeIndexModal(
(objs) => {
Promise.map(conflictedIndexPatterns, resolveConflicts.bind(null, objs))
.then(Promise.map(conflictedSavedObjectsLinkedToSavedSearches, saveObject))
.then(resolve)
.catch(reject);
},

View file

@ -5,117 +5,121 @@ export default function ({ getService, getPageObjects }) {
const retry = getService('retry');
const kibanaServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
const PageObjects = getPageObjects(['common', 'settings']);
const PageObjects = getPageObjects(['common', 'settings', 'header']);
describe('import objects', function describeIndexTests() {
before(async function () {
beforeEach(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 () {
afterEach(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.header.waitUntilLoadingHasFinished();
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.header.waitUntilLoadingHasFinished();
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);
});
// Flaky: https://github.com/elastic/kibana/issues/15913
// 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);
// });
// Flaky: https://github.com/elastic/kibana/issues/15913
// 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);
// });
it('should handle saved searches and objects with saved searches properly', async function () {
await PageObjects.settings.navigateTo();
it('should allow for overrides', async function () {
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects_with_saved_searches.json'));
await PageObjects.common.clickConfirmOnModal();
// Put in data which already exists
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects_exists.json'));
// Say we want to be asked
await PageObjects.common.clickCancelOnModal();
// Interact with the conflict modal
await PageObjects.settings.setImportIndexFieldOption(2);
await PageObjects.settings.clickChangeIndexConfirmButton();
await PageObjects.settings.clickVisualizationsTab();
// Now confirm we want to override
await PageObjects.common.clickConfirmOnModal();
await PageObjects.header.waitUntilLoadingHasFinished();
const vizRowCount = await retry.try(async () => {
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(vizRowCount).to.be(2);
expect(rowCount).to.be(1);
});
it('should allow for cancelling overrides', async function () {
await PageObjects.settings.clickKibanaSavedObjects();
// Put in data which already exists
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects_exists.json'));
// Say we want to be asked
await PageObjects.common.clickCancelOnModal();
// Interact with the conflict modal
await PageObjects.settings.setImportIndexFieldOption(2);
await PageObjects.settings.clickChangeIndexConfirmButton();
// Now cancel the override
await PageObjects.common.clickCancelOnModal();
await PageObjects.settings.clickVisualizationsTab();
const rowCount = await retry.try(async () => {
const rows = await PageObjects.settings.getVisualizationRows();
return rows.length;
});
expect(rowCount).to.be(1);
});
it('should handle saved searches and objects with saved searches properly', async function () {
// First, import the saved search
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects_saved_search.json'));
await PageObjects.common.clickConfirmOnModal();
// Second, we need to delete the index pattern
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaIndices();
await PageObjects.settings.clickOnOnlyIndexPattern();
await PageObjects.settings.removeIndexPattern();
// Last, import a saved object connected to the saved search
// This should NOT show the modal
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects_connected_to_saved_search.json'));
await PageObjects.common.clickConfirmOnModal();
await PageObjects.header.waitUntilLoadingHasFinished();
await PageObjects.settings.clickVisualizationsTab();
const vizRowCount = await retry.try(async () => {
const rows = await PageObjects.settings.getVisualizationRows();
return rows.length;
});
expect(vizRowCount).to.be(1);
await PageObjects.settings.clickSearchesTab();
const searchRowCount = await retry.try(async () => {
const rows = await PageObjects.settings.getVisualizationRows();
if (rows.length !== 1) {
throw 'Not loaded yet';
}
return rows.length;
});
expect(searchRowCount).to.be(1);

View file

@ -1,27 +1,4 @@
[
{
"_id": "c45e6c50-ba72-11e7-a8f9-ad70f02e633d",
"_type": "search",
"_source": {
"title": "PHP saved search",
"description": "",
"hits": 0,
"columns": [
"_source"
],
"sort": [
"@timestamp",
"desc"
],
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"f0df0960-ae8d-11e7-9c8d-53400275d89a\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"php\"},\"filter\":[]}"
}
},
"_meta": {
"savedObjectVersion": 2
}
},
{
"_id": "cbd520f0-ba72-11e7-a8f9-ad70f02e633d",
"_type": "visualization",

View file

@ -0,0 +1,19 @@
[
{
"_id": "Shared-Item-Visualization-AreaChart",
"_type": "visualization",
"_source": {
"title": "Shared-Item Visualization AreaChart",
"visState": "{\"title\":\"New Visualization\",\"type\":\"area\",\"params\":{\"shareYAxis\":true,\"addTooltip\":true,\"addLegend\":true,\"smoothLines\":false,\"scale\":\"linear\",\"interpolate\":\"linear\",\"mode\":\"stacked\",\"times\":[],\"addTimeMarker\":false,\"defaultYExtents\":false,\"setYExtents\":false,\"yAxis\":{}},\"aggs\":[{\"id\":\"1\",\"type\":\"count\",\"schema\":\"metric\",\"params\":{}},{\"id\":\"2\",\"type\":\"date_histogram\",\"schema\":\"segment\",\"params\":{\"field\":\"@timestamp\",\"interval\":\"auto\",\"customInterval\":\"2h\",\"min_doc_count\":1,\"extended_bounds\":{}}}],\"listeners\":{}}",
"uiStateJSON": "{}",
"description": "AreaChart",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"logstash-*\",\"query\":{\"query_string\":{\"query\":\"*\",\"analyze_wildcard\":true}},\"filter\":[]}"
}
},
"_meta": {
"savedObjectVersion": 2
}
}
]

View file

@ -0,0 +1,25 @@
[
{
"_id": "c45e6c50-ba72-11e7-a8f9-ad70f02e633d",
"_type": "search",
"_source": {
"title": "PHP saved search",
"description": "",
"hits": 0,
"columns": [
"_source"
],
"sort": [
"@timestamp",
"desc"
],
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"index\":\"f1e4c910-a2e6-11e7-bb30-233be9be6a15\",\"highlightAll\":true,\"version\":true,\"query\":{\"language\":\"lucene\",\"query\":\"php\"},\"filter\":[]}"
}
},
"_meta": {
"savedObjectVersion": 2
}
}
]

View file

@ -346,6 +346,10 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
return await testSubjects.find('createIndexPatternCreateButton');
}
async clickOnOnlyIndexPattern() {
return await testSubjects.click('indexPatternLink');
}
async removeIndexPattern() {
let alertText;
await retry.try(async () => {