[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:
parent
b27c5772cf
commit
f93b76285b
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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",
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
|
@ -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
|
||||
}
|
||||
}
|
||||
]
|
|
@ -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 () => {
|
||||
|
|
Loading…
Reference in a new issue