Add add missing saved object reference handling for searchSourceJSON filter attribute (#29840)

* Add missing references for searchSourceJSON filter

* Add tests for searchSourceJSON filter, more tests for index attribute as well

* Define indexRefName once

* Fix broken test, avoid mutating searchSourceFields

* Fix bug in code change
This commit is contained in:
Mike Côté 2019-02-05 13:11:27 -05:00 committed by GitHub
parent 3ccf6793df
commit 6b74470eae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 367 additions and 26 deletions

View file

@ -31,16 +31,29 @@ function migrateIndexPattern(doc) {
// Let it go, the data is invalid and we'll leave it as is
return;
}
if (!searchSource.index) {
return;
if (searchSource.index) {
searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
doc.references.push({
name: searchSource.indexRefName,
type: 'index-pattern',
id: searchSource.index,
});
delete searchSource.index;
}
if (searchSource.filter) {
searchSource.filter.forEach((filterRow, i) => {
if (!filterRow.meta || !filterRow.meta.index) {
return;
}
filterRow.meta.indexRefName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`;
doc.references.push({
name: filterRow.meta.indexRefName,
type: 'index-pattern',
id: filterRow.meta.index,
});
delete filterRow.meta.index;
});
}
doc.references.push({
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
type: 'index-pattern',
id: searchSource.index,
});
searchSource.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
delete searchSource.index;
doc.attributes.kibanaSavedObjectMeta.searchSourceJSON = JSON.stringify(searchSource);
}

View file

@ -189,7 +189,7 @@ Object {
`);
});
it('skips error when "index" is missing from searchSourceJSON', () => {
it('skips error when "index" and "filter" is missing from searchSourceJSON', () => {
const doc = {
id: '1',
type: 'visualization',
@ -264,6 +264,55 @@ Object {
`);
});
it('extracts index patterns from the filter', () => {
const doc = {
id: '1',
type: 'visualization',
attributes: {
visState: '{}',
kibanaSavedObjectMeta: {
searchSourceJSON: JSON.stringify({
bar: true,
filter: [
{
meta: { index: 'my-index', foo: true },
},
],
}),
},
savedSearchId: '123',
},
};
const migratedDoc = migrate(doc);
/* eslint-disable max-len */
expect(migratedDoc).toMatchInlineSnapshot(`
Object {
"attributes": Object {
"kibanaSavedObjectMeta": Object {
"searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}",
},
"savedSearchRefName": "search_0",
"visState": "{}",
},
"id": "1",
"references": Array [
Object {
"id": "my-index",
"name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index",
"type": "index-pattern",
},
Object {
"id": "123",
"name": "search_0",
"type": "search",
},
],
"type": "visualization",
}
`);
/* eslint-enable max-len */
});
it('skips extracting savedSearchId when missing', () => {
const doc = {
id: '1',
@ -634,7 +683,7 @@ Object {
`);
});
test('skips error when "index" is missing from searchSourceJSON', () => {
test('skips error when "index" and "filter" is missing from searchSourceJSON', () => {
const doc = {
id: '1',
type: 'dashboard',
@ -717,6 +766,62 @@ Object {
`);
});
test('extracts index patterns from filter', () => {
const doc = {
id: '1',
type: 'dashboard',
attributes: {
kibanaSavedObjectMeta: {
searchSourceJSON: JSON.stringify({
bar: true,
filter: [
{
meta: {
foo: true,
index: 'my-index',
},
},
],
}),
},
panelsJSON:
'[{"id":"1","type":"visualization","foo":true},{"id":"2","type":"visualization","bar":true}]',
},
};
const migratedDoc = migration(doc);
/* eslint-disable max-len */
expect(migratedDoc).toMatchInlineSnapshot(`
Object {
"attributes": Object {
"kibanaSavedObjectMeta": Object {
"searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}",
},
"panelsJSON": "[{\\"foo\\":true,\\"panelRefName\\":\\"panel_0\\"},{\\"bar\\":true,\\"panelRefName\\":\\"panel_1\\"}]",
},
"id": "1",
"references": Array [
Object {
"id": "my-index",
"name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index",
"type": "index-pattern",
},
Object {
"id": "1",
"name": "panel_0",
"type": "visualization",
},
Object {
"id": "2",
"name": "panel_1",
"type": "visualization",
},
],
"type": "dashboard",
}
`);
/* eslint-enable max-len */
});
test('skips error when panelsJSON is not a string', () => {
const doc = {
id: '1',
@ -950,7 +1055,7 @@ Object {
`);
});
test('skips error when "index" is missing from searchSourceJSON', () => {
test('skips error when "index" and "filter" is missing from searchSourceJSON', () => {
const doc = {
id: '123',
type: 'search',
@ -1009,5 +1114,50 @@ Object {
}
`);
});
test('extracts index patterns from filter', () => {
const doc = {
id: '123',
type: 'search',
attributes: {
foo: true,
kibanaSavedObjectMeta: {
searchSourceJSON: JSON.stringify({
bar: true,
filter: [
{
meta: {
foo: true,
index: 'my-index',
},
},
],
}),
},
},
};
const migratedDoc = migration(doc);
/* eslint-disable max-len */
expect(migratedDoc).toMatchInlineSnapshot(`
Object {
"attributes": Object {
"foo": true,
"kibanaSavedObjectMeta": Object {
"searchSourceJSON": "{\\"bar\\":true,\\"filter\\":[{\\"meta\\":{\\"foo\\":true,\\"indexRefName\\":\\"kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index\\"}}]}",
},
},
"id": "123",
"references": Array [
Object {
"id": "my-index",
"name": "kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index",
"type": "index-pattern",
},
],
"type": "search",
}
`);
/* eslint-enable max-len */
});
});
});

View file

@ -292,13 +292,16 @@ describe('Saved Object', function () {
});
});
describe('with extractReferences', () => {
it('calls the function', async () => {
describe('to extract references', () => {
it('when "extractReferences" function when passed in', async () => {
const id = '123';
stubESResponse(getMockedDocResponse('id'));
let extractReferencesCallCount = 0;
stubESResponse(getMockedDocResponse(id));
const extractReferences = ({ attributes, references }) => {
extractReferencesCallCount++;
references.push({
name: 'test',
type: 'index-pattern',
id: 'my-index',
});
return { attributes, references };
};
return createInitializedSavedObject({ type: 'dashboard', extractReferences })
@ -310,10 +313,95 @@ describe('Saved Object', function () {
type: 'dashboard',
});
});
return savedObject.save();
})
.then(() => {
expect(extractReferencesCallCount).to.be(1);
return savedObject
.save()
.then(() => {
const { references } = savedObjectsClientStub.create.getCall(0).args[2];
expect(references).to.have.length(1);
expect(references[0]).to.eql({
name: 'test',
type: 'index-pattern',
id: 'my-index',
});
});
});
});
it('when index exists in searchSourceJSON', () => {
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.setField('index', new IndexPattern('my-index', null, []));
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: 'my-index',
});
});
});
});
it('when indexes exists in filter of searchSourceJSON', () => {
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.setField('filter', [{
meta: {
index: 'my-index',
}
}]);
return savedObject
.save()
.then(() => {
expect(savedObjectsClientStub.create.getCall(0).args[1]).to.eql({
kibanaSavedObjectMeta: {
searchSourceJSON: JSON.stringify({
filter: [
{
meta: {
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
}
}
],
}),
},
});
const { references } = savedObjectsClientStub.create.getCall(0).args[2];
expect(references).to.have.length(1);
expect(references[0]).to.eql({
name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
type: 'index-pattern',
id: 'my-index',
});
});
});
});
});
@ -504,6 +592,54 @@ describe('Saved Object', function () {
expect(injectReferences).to.have.property('calledOnce', true);
});
});
it('injects references from searchSourceJSON', async () => {
const savedObject = new SavedObject({ type: 'dashboard', searchSource: true });
return savedObject
.init()
.then(() => {
const response = {
found: true,
_source: {
kibanaSavedObjectMeta: {
searchSourceJSON: JSON.stringify({
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.index',
filter: [
{
meta: {
indexRefName: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
},
},
],
}),
},
},
references: [
{
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
type: 'index-pattern',
id: 'my-index-1',
},
{
name: 'kibanaSavedObjectMeta.searchSourceJSON.filter[0].meta.index',
type: 'index-pattern',
id: 'my-index-2',
},
],
};
savedObject.applyESResp(response);
expect(savedObject.searchSource.getFields()).to.eql({
index: 'my-index-1',
filter: [
{
meta: {
index: 'my-index-2',
},
},
],
});
});
});
});
describe ('config', function () {

View file

@ -148,6 +148,20 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
delete searchSourceValues.indexRefName;
}
if (searchSourceValues.filter) {
searchSourceValues.filter.forEach((filterRow) => {
if (!filterRow.meta || !filterRow.meta.indexRefName) {
return;
}
const reference = references.find(reference => reference.name === filterRow.meta.indexRefName);
if (!reference) {
throw new Error(`Could not find reference for ${filterRow.meta.indexRefName} on ${this.getEsType()}`);
}
filterRow.meta.index = reference.id;
delete filterRow.meta.indexRefName;
});
}
const searchSourceFields = this.searchSource.getFields();
const fnProps = _.transform(searchSourceFields, function (dynamic, val, name) {
if (_.isFunction(val)) dynamic[name] = val;
@ -296,16 +310,44 @@ export function SavedObjectProvider(Promise, Private, Notifier, confirmModalProm
});
if (this.searchSource) {
const searchSourceFields = _.omit(this.searchSource.getFields(), ['sort', 'size']);
let searchSourceFields = _.omit(this.searchSource.getFields(), ['sort', 'size']);
if (searchSourceFields.index) {
const indexId = searchSourceFields.index;
searchSourceFields.indexRefName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
const { id: indexId } = searchSourceFields.index;
const refName = 'kibanaSavedObjectMeta.searchSourceJSON.index';
references.push({
name: 'kibanaSavedObjectMeta.searchSourceJSON.index',
name: refName,
type: 'index-pattern',
id: indexId,
});
delete searchSourceFields.index;
searchSourceFields = {
...searchSourceFields,
indexRefName: refName,
index: undefined,
};
}
if (searchSourceFields.filter) {
searchSourceFields = {
...searchSourceFields,
filter: searchSourceFields.filter.map((filterRow, i) => {
if (!filterRow.meta || !filterRow.meta.index) {
return filterRow;
}
const refName = `kibanaSavedObjectMeta.searchSourceJSON.filter[${i}].meta.index`;
references.push({
name: refName,
type: 'index-pattern',
id: filterRow.meta.index,
});
return {
...filterRow,
meta: {
...filterRow.meta,
indexRefName: refName,
index: undefined,
}
};
}),
};
}
attributes.kibanaSavedObjectMeta = {
searchSourceJSON: angular.toJson(searchSourceFields)