mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* Resolve filter index references when importing saved objects * Tests for filter index resolution * saved_object.js test for serialization of non-existing searchSource indexes
This commit is contained in:
parent
c0c922355c
commit
a10c93ec5c
7 changed files with 160 additions and 12 deletions
|
@ -435,6 +435,17 @@ exports[`Flyout legacy conflicts should allow conflict resolution 1`] = `
|
|||
],
|
||||
"newIndexPatternId": undefined,
|
||||
},
|
||||
Object {
|
||||
"existingIndexPatternId": "filterIndex",
|
||||
"list": Array [
|
||||
Object {
|
||||
"id": "filterIndex",
|
||||
"title": "MyIndexPattern*",
|
||||
"type": "index-pattern",
|
||||
},
|
||||
],
|
||||
"newIndexPatternId": undefined,
|
||||
},
|
||||
]
|
||||
}
|
||||
pagination={
|
||||
|
|
|
@ -418,8 +418,12 @@ describe('Flyout', () => {
|
|||
},
|
||||
obj: {
|
||||
searchSource: {
|
||||
getOwnField: () => 'MyIndexPattern*',
|
||||
getOwnField: (field) => {
|
||||
if(field === 'index') { return 'MyIndexPattern*';}
|
||||
if(field === 'filter') { return [{ meta: { index: 'filterIndex' } }];}
|
||||
},
|
||||
},
|
||||
_serialize: () => { return { references: [{ id: 'MyIndexPattern*' }, { id: 'filterIndex' }] };},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
@ -477,7 +481,17 @@ describe('Flyout', () => {
|
|||
type: 'index-pattern',
|
||||
},
|
||||
],
|
||||
},
|
||||
}, {
|
||||
'existingIndexPatternId': 'filterIndex',
|
||||
'list': [
|
||||
{
|
||||
'id': 'filterIndex',
|
||||
'title': 'MyIndexPattern*',
|
||||
'type': 'index-pattern',
|
||||
},
|
||||
],
|
||||
'newIndexPatternId': undefined,
|
||||
}
|
||||
],
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { groupBy, take, get as getField } from 'lodash';
|
||||
import { take, get as getField } from 'lodash';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutBody,
|
||||
|
@ -273,9 +273,15 @@ class FlyoutUI extends Component {
|
|||
confirmModalPromise
|
||||
);
|
||||
|
||||
const byId = groupBy(conflictedIndexPatterns, ({ obj }) =>
|
||||
obj.searchSource.getOwnField('index')
|
||||
);
|
||||
const byId = {};
|
||||
conflictedIndexPatterns
|
||||
.map(({ doc, obj }) => {
|
||||
return { doc, obj: obj._serialize() };
|
||||
}).forEach(({ doc, obj }) =>
|
||||
obj.references.forEach(ref => {
|
||||
byId[ref.id] = byId[ref.id] != null ? byId[ref.id].concat({ doc, obj }) : [{ doc, obj }];
|
||||
})
|
||||
);
|
||||
const unmatchedReferences = Object.entries(byId).reduce(
|
||||
(accum, [existingIndexPatternId, list]) => {
|
||||
accum.push({
|
||||
|
|
|
@ -238,7 +238,9 @@ describe('resolveSavedObjects', () => {
|
|||
{
|
||||
obj: {
|
||||
searchSource: {
|
||||
getOwnField: () => '1',
|
||||
getOwnField: (field) => {
|
||||
return field === 'index' ? '1' : undefined;
|
||||
},
|
||||
},
|
||||
hydrateIndexPattern,
|
||||
save,
|
||||
|
@ -247,7 +249,9 @@ describe('resolveSavedObjects', () => {
|
|||
{
|
||||
obj: {
|
||||
searchSource: {
|
||||
getOwnField: () => '3',
|
||||
getOwnField: (field) => {
|
||||
return field === 'index' ? '3' : undefined;
|
||||
},
|
||||
},
|
||||
hydrateIndexPattern,
|
||||
save,
|
||||
|
@ -283,6 +287,63 @@ describe('resolveSavedObjects', () => {
|
|||
expect(hydrateIndexPattern).toHaveBeenCalledWith('2');
|
||||
expect(hydrateIndexPattern).toHaveBeenCalledWith('4');
|
||||
});
|
||||
|
||||
it('should resolve filter index conflicts', async () => {
|
||||
const hydrateIndexPattern = jest.fn();
|
||||
const save = jest.fn();
|
||||
|
||||
const conflictedIndexPatterns = [
|
||||
{
|
||||
obj: {
|
||||
searchSource: {
|
||||
getOwnField: (field) => {
|
||||
return field === 'index' ? '1' : [{ meta: { index: 'filterIndex' } }];
|
||||
},
|
||||
setField: jest.fn(),
|
||||
},
|
||||
hydrateIndexPattern,
|
||||
save,
|
||||
},
|
||||
},
|
||||
{
|
||||
obj: {
|
||||
searchSource: {
|
||||
getOwnField: (field) => {
|
||||
return field === 'index' ? '3' : undefined;
|
||||
},
|
||||
},
|
||||
hydrateIndexPattern,
|
||||
save,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const resolutions = [
|
||||
{
|
||||
oldId: '1',
|
||||
newId: '2',
|
||||
},
|
||||
{
|
||||
oldId: '3',
|
||||
newId: '4',
|
||||
},
|
||||
{
|
||||
oldId: 'filterIndex',
|
||||
newId: 'newFilterIndex',
|
||||
},
|
||||
];
|
||||
|
||||
const overwriteAll = false;
|
||||
|
||||
await resolveIndexPatternConflicts(
|
||||
resolutions,
|
||||
conflictedIndexPatterns,
|
||||
overwriteAll
|
||||
);
|
||||
|
||||
expect(conflictedIndexPatterns[0].obj.searchSource.setField).toHaveBeenCalledWith('filter', [{ meta: { index: 'newFilterIndex' } }]);
|
||||
expect(save.mock.calls.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveObjects', () => {
|
||||
|
|
|
@ -140,19 +140,38 @@ export async function resolveIndexPatternConflicts(
|
|||
overwriteAll
|
||||
) {
|
||||
let importCount = 0;
|
||||
|
||||
await awaitEachItemInParallel(conflictedIndexPatterns, async ({ obj }) => {
|
||||
// Resolve search index reference:
|
||||
let oldIndexId = obj.searchSource.getOwnField('index');
|
||||
// Depending on the object, this can either be the raw id or the actual index pattern object
|
||||
if (typeof oldIndexId !== 'string') {
|
||||
oldIndexId = oldIndexId.id;
|
||||
}
|
||||
const resolution = resolutions.find(({ oldId }) => oldId === oldIndexId);
|
||||
let resolution = resolutions.find(({ oldId }) => oldId === oldIndexId);
|
||||
if (resolution) {
|
||||
const newIndexId = resolution.newId;
|
||||
await obj.hydrateIndexPattern(newIndexId);
|
||||
}
|
||||
|
||||
// Resolve filter index reference:
|
||||
const filter = (obj.searchSource.getOwnField('filter') || []).map((filter) => {
|
||||
if (!(filter.meta && filter.meta.index)) {
|
||||
return filter;
|
||||
}
|
||||
|
||||
resolution = resolutions.find(({ oldId }) => oldId === filter.meta.index);
|
||||
return resolution ? ({ ...filter, ...{ meta: { ...filter.meta, index: resolution.newId } } }) : filter;
|
||||
});
|
||||
|
||||
if (filter.length > 0) {
|
||||
obj.searchSource.setField('filter', filter);
|
||||
}
|
||||
|
||||
if (!resolution) {
|
||||
// The user decided to skip this conflict so do nothing
|
||||
return;
|
||||
}
|
||||
const newIndexId = resolution.newId;
|
||||
await obj.hydrateIndexPattern(newIndexId);
|
||||
if (await saveObject(obj, overwriteAll)) {
|
||||
importCount++;
|
||||
}
|
||||
|
|
|
@ -361,6 +361,40 @@ describe('Saved Object', function () {
|
|||
});
|
||||
});
|
||||
|
||||
it('when index in searchSourceJSON is not found', () => {
|
||||
const id = '123';
|
||||
stubESResponse(getMockedDocResponse(id));
|
||||
return createInitializedSavedObject({ type: 'dashboard', searchSource: true })
|
||||
.then((savedObject) => {
|
||||
sinon.stub(savedObjectsClientStub, 'create').callsFake(() => {
|
||||
return BluebirdPromise.resolve({
|
||||
id,
|
||||
version: 2,
|
||||
type: 'dashboard',
|
||||
});
|
||||
});
|
||||
savedObject.searchSource.setFields({ 'index': 'non-existant-index' });
|
||||
return savedObject
|
||||
.save()
|
||||
.then(() => {
|
||||
expect(savedObjectsClientStub.create.getCall(0).args[1]).to.eql({
|
||||
kibanaSavedObjectMeta: {
|
||||
searchSourceJSON: JSON.stringify({
|
||||
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
}),
|
||||
},
|
||||
});
|
||||
const { references } = savedObjectsClientStub.create.getCall(0).args[2];
|
||||
expect(references).to.have.length(1);
|
||||
expect(references[0]).to.eql({
|
||||
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
|
||||
type: 'index-pattern',
|
||||
id: 'non-existant-index',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('when indexes exists in filter of searchSourceJSON', () => {
|
||||
const id = '123';
|
||||
stubESResponse(getMockedDocResponse(id));
|
||||
|
|
|
@ -321,7 +321,10 @@ export function SavedObjectProvider(Promise, Private, confirmModalPromise, index
|
|||
if (this.searchSource) {
|
||||
let searchSourceFields = _.omit(this.searchSource.getFields(), ['sort', 'size']);
|
||||
if (searchSourceFields.index) {
|
||||
const { id: indexId } = searchSourceFields.index;
|
||||
// searchSourceFields.index will normally be an IndexPattern, but can be a string in two scenarios:
|
||||
// (1) `init()` (and by extension `hydrateIndexPattern()`) hasn't been called on this Saved Object
|
||||
// (2) The IndexPattern doesn't exist, so we fail to resolve it in `hydrateIndexPattern()`
|
||||
const indexId = typeof (searchSourceFields.index) === 'string' ? searchSourceFields.index : searchSourceFields.index.id;
|
||||
const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
|
||||
references.push({
|
||||
name: refName,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue