Skip to content

Commit

Permalink
[Management] Handle saved search import better (#14625)
Browse files Browse the repository at this point in the history
* Handle saved search import better

* Add functional test

* Better comments
  • Loading branch information
chrisronline authored and tylersmalley committed Oct 27, 2017
1 parent 8a5fce4 commit 6ef803b
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,15 @@ uiModules.get('apps/management')
);
})
.then((overwriteAll) => {
// Keep a record of the index patterns assigned to our imported saved objects that do not
// exist. We will provide a way for the user to manually select a new index pattern for those
// saved objects.
const conflictedIndexPatterns = [];
// 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 = [];

function importDocument(doc) {
function importDocument(swallowErrors, doc) {
const { service } = find($scope.services, { type: doc._type }) || {};

if (!service) {
Expand All @@ -228,11 +234,16 @@ uiModules.get('apps/management')
return obj.save({ confirmOverwrite : !overwriteAll });
})
.catch((err) => {
if (err instanceof SavedObjectNotFound && err.savedObjectType === 'index-pattern') {
conflictedIndexPatterns.push({ obj, doc });
return;
if (swallowErrors && err instanceof SavedObjectNotFound) {
switch (err.savedObjectType) {
case 'search':
conflictedSearchDocs.push(doc);
return;
case '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);
Expand All @@ -258,35 +269,39 @@ uiModules.get('apps/management')
}, defaultDocTypes);
}

function resolveConflicts(objs, { obj }) {
const oldIndexId = obj.searchSource.getOwn('index');
const newIndexId = objs.find(({ oldId }) => oldId === oldIndexId).newId;
// If the user did not select a new index pattern in the modal, the id
// will be same as before, so don't try to update it
if (newIndexId === oldIndexId) {
return;
}
return obj.hydrateIndexPattern(newIndexId)
.then(() => obj.save({ confirmOverwrite : !overwriteAll }));
}

const docTypes = groupByType(docs);

return Promise.map(docTypes.searches, importDocument)
.then(() => Promise.map(docTypes.other, importDocument))
return Promise.map(docTypes.searches, importDocument.bind(null, true))
.then(() => Promise.map(docTypes.other, importDocument.bind(null, true)))
.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();
return new Promise((resolve, reject) => {
showChangeIndexModal(
(objs) => {
Promise.map(conflictedIndexPatterns, resolveConflicts.bind(null, objs))
.then(resolve)
.catch(reject);
},
conflictedIndexPatterns,
$route.current.locals.indexPatterns,
);
});
}
})
.then(() => Promise.map(conflictedSearchDocs, importDocument.bind(null, false)))
.then(refreshData)
.catch(notify.error);
});
};
Expand Down
29 changes: 29 additions & 0 deletions test/functional/apps/management/_import_objects.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,34 @@ export default function ({ getService, getPageObjects }) {
});
expect(rowCount).to.be(2);
});

it('should handle saved searches and objects with saved searches properly', async function () {
await PageObjects.settings.navigateTo();
await PageObjects.settings.clickKibanaSavedObjects();
await PageObjects.settings.importFile(path.join(__dirname, 'exports', '_import_objects_with_saved_searches.json'));
await PageObjects.common.clickConfirmOnModal();
await PageObjects.settings.setImportIndexFieldOption(2);
await PageObjects.settings.clickChangeIndexConfirmButton();
await PageObjects.settings.clickVisualizationsTab();

const vizRowCount = 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);

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);
});
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
{
"_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",
"_source": {
"title": "PHP Viz",
"visState": "{\"title\":\"PHP Viz\",\"type\":\"horizontal_bar\",\"params\":{\"type\":\"histogram\",\"grid\":{\"categoryLines\":false,\"style\":{\"color\":\"#eee\"}},\"categoryAxes\":[{\"id\":\"CategoryAxis-1\",\"type\":\"category\",\"position\":\"left\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\"},\"labels\":{\"show\":true,\"rotate\":0,\"filter\":false,\"truncate\":200},\"title\":{}}],\"valueAxes\":[{\"id\":\"ValueAxis-1\",\"name\":\"LeftAxis-1\",\"type\":\"value\",\"position\":\"bottom\",\"show\":true,\"style\":{},\"scale\":{\"type\":\"linear\",\"mode\":\"normal\"},\"labels\":{\"show\":true,\"rotate\":75,\"filter\":true,\"truncate\":100},\"title\":{\"text\":\"Count\"}}],\"seriesParams\":[{\"show\":true,\"type\":\"histogram\",\"mode\":\"normal\",\"data\":{\"label\":\"Count\",\"id\":\"1\"},\"valueAxis\":\"ValueAxis-1\",\"drawLinesBetweenPoints\":true,\"showCircles\":true}],\"addTooltip\":true,\"addLegend\":true,\"legendPosition\":\"right\",\"times\":[],\"addTimeMarker\":false},\"aggs\":[{\"id\":\"1\",\"enabled\":true,\"type\":\"count\",\"schema\":\"metric\",\"params\":{}}]}",
"uiStateJSON": "{}",
"description": "",
"savedSearchId": "c45e6c50-ba72-11e7-a8f9-ad70f02e633d",
"version": 1,
"kibanaSavedObjectMeta": {
"searchSourceJSON": "{\"filter\":[],\"query\":{\"query\":\"\",\"language\":\"lucene\"}}"
}
},
"_meta": {
"savedObjectVersion": 2
}
}
]
4 changes: 4 additions & 0 deletions test/functional/page_objects/settings_page.js
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,10 @@ export function SettingsPageProvider({ getService, getPageObjects }) {
await (await testSubjects.find('objectsTab-visualizations')).click();
}

async clickSearchesTab() {
await (await testSubjects.find('objectsTab-searches')).click();
}

async getVisualizationRows() {
return await testSubjects.findAll(`objectsTableRow`);
}
Expand Down

0 comments on commit 6ef803b

Please sign in to comment.