[Data plugin] Wrong caching for Index pattern fields (#87116) (#87376)

* [Data plugin] Wrong caching for Index pattern fields

Closes: #84666

* remove can update index_pattern fields test

* fix tests

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
# Conflicts:
#	src/plugins/data/common/index_patterns/index_patterns/index_patterns.ts
This commit is contained in:
Alexey Antonov 2021-01-06 09:19:53 +03:00 committed by GitHub
parent 4867610afb
commit 41aab669bc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 202 deletions

View file

@ -47,7 +47,6 @@ import { IndexPatternMissingIndices } from '../lib';
import { findByTitle } from '../utils';
import { DuplicateIndexPatternError } from '../errors';
const indexPatternCache = createIndexPatternCache();
const MAX_ATTEMPTS_TO_RESOLVE_CONFLICTS = 3;
const savedObjectType = 'index-pattern';
@ -75,6 +74,7 @@ export class IndexPatternsService {
private onNotification: OnNotification;
private onError: OnError;
private onUnsupportedTimePattern: OnUnsupportedTimePattern;
private indexPatternCache: ReturnType<typeof createIndexPatternCache>;
ensureDefaultIndexPattern: EnsureDefaultIndexPattern;
constructor({
@ -98,6 +98,8 @@ export class IndexPatternsService {
uiSettings,
onRedirectNoIndexPattern
);
this.indexPatternCache = createIndexPatternCache();
}
/**
@ -180,9 +182,9 @@ export class IndexPatternsService {
clearCache = (id?: string) => {
this.savedObjectsCache = null;
if (id) {
indexPatternCache.clear(id);
this.indexPatternCache.clear(id);
} else {
indexPatternCache.clearAll();
this.indexPatternCache.clearAll();
}
};
@ -468,11 +470,12 @@ export class IndexPatternsService {
get = async (id: string): Promise<IndexPattern> => {
const indexPatternPromise =
indexPatternCache.get(id) || indexPatternCache.set(id, this.getSavedObjectAndInit(id));
this.indexPatternCache.get(id) ||
this.indexPatternCache.set(id, this.getSavedObjectAndInit(id));
// don't cache failed requests
indexPatternPromise.catch(() => {
indexPatternCache.clear(id);
this.indexPatternCache.clear(id);
});
return indexPatternPromise;
@ -536,7 +539,7 @@ export class IndexPatternsService {
id: indexPattern.id,
});
indexPattern.id = response.id;
indexPatternCache.set(indexPattern.id, Promise.resolve(indexPattern));
this.indexPatternCache.set(indexPattern.id, Promise.resolve(indexPattern));
return indexPattern;
}
@ -619,7 +622,7 @@ export class IndexPatternsService {
indexPattern.version = samePattern.version;
// Clear cache
indexPatternCache.clear(indexPattern.id!);
this.indexPatternCache.clear(indexPattern.id!);
// Try the save again
return this.updateSavedObject(indexPattern, saveAttempts, ignoreErrors);
@ -633,7 +636,7 @@ export class IndexPatternsService {
* @param indexPatternId: Id of kibana Index Pattern to delete
*/
async delete(indexPatternId: string) {
indexPatternCache.clear(indexPatternId);
this.indexPatternCache.clear(indexPatternId);
return this.savedObjectsClient.delete('index-pattern', indexPatternId);
}
}

View file

@ -22,8 +22,32 @@ import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
describe('main', () => {
const basicIndex = 'ba*ic_index';
let indexPattern: any;
before(async () => {
await esArchiver.load('index_patterns/basic_index');
indexPattern = (
await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title: basicIndex,
},
})
).body.index_pattern;
});
after(async () => {
await esArchiver.unload('index_patterns/basic_index');
if (indexPattern) {
await supertest.delete('/api/index_patterns/index_pattern/' + indexPattern.id);
}
});
it('can update multiple fields', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
@ -171,48 +195,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(response3.status).to.be(200);
expect(response3.body.index_pattern.fieldAttrs.foo.count).to.be(undefined);
});
it('can set field "count" attribute on an existing field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
fields: {
foo: {
name: 'foo',
type: 'string',
count: 123,
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body.index_pattern.fieldAttrs.foo).to.be(undefined);
expect(response1.body.index_pattern.fields.foo.count).to.be(123);
const response2 = await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`)
.send({
fields: {
foo: {
count: 456,
},
},
});
expect(response2.status).to.be(200);
expect(response2.body.index_pattern.fieldAttrs.foo).to.be(undefined);
expect(response2.body.index_pattern.fields.foo.count).to.be(456);
const response3 = await supertest.get(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}`
);
expect(response3.status).to.be(200);
expect(response3.body.index_pattern.fieldAttrs.foo).to.be(undefined);
expect(response3.body.index_pattern.fields.foo.count).to.be(456);
});
});
describe('customLabel', () => {
@ -323,46 +305,20 @@ export default function ({ getService }: FtrProviderContext) {
});
it('can set field "customLabel" attribute on an existing field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
fields: {
foo: {
name: 'foo',
type: 'string',
count: 123,
customLabel: 'foo',
},
await supertest.post(`/api/index_patterns/index_pattern/${indexPattern.id}/fields`).send({
fields: {
foo: {
customLabel: 'baz',
},
},
});
expect(response1.status).to.be(200);
expect(response1.body.index_pattern.fieldAttrs.foo).to.be(undefined);
expect(response1.body.index_pattern.fields.foo.customLabel).to.be('foo');
const response2 = await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`)
.send({
fields: {
foo: {
customLabel: 'baz',
},
},
});
expect(response2.status).to.be(200);
expect(response2.body.index_pattern.fieldAttrs.foo).to.be(undefined);
expect(response2.body.index_pattern.fields.foo.customLabel).to.be('baz');
const response3 = await supertest.get(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}`
const response1 = await supertest.get(
`/api/index_patterns/index_pattern/${indexPattern.id}`
);
expect(response3.status).to.be(200);
expect(response3.body.index_pattern.fieldAttrs.foo).to.be(undefined);
expect(response3.body.index_pattern.fields.foo.customLabel).to.be('baz');
expect(response1.status).to.be(200);
expect(response1.body.index_pattern.fields.foo.customLabel).to.be('baz');
});
});
@ -463,31 +419,8 @@ export default function ({ getService }: FtrProviderContext) {
});
it('can remove "format" attribute from index_pattern format map', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
fieldFormats: {
foo: {
id: 'bar',
params: {
baz: 'qux',
},
},
},
},
});
expect(response1.status).to.be(200);
expect(response1.body.index_pattern.fieldFormats.foo).to.eql({
id: 'bar',
params: {
baz: 'qux',
},
});
const response2 = await supertest
.post(`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/fields`)
.post(`/api/index_patterns/index_pattern/${indexPattern.id}/fields`)
.send({
fields: {
foo: {
@ -500,7 +433,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(response2.body.index_pattern.fieldFormats.foo).to.be(undefined);
const response3 = await supertest.get(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}`
`/api/index_patterns/index_pattern/${indexPattern.id}`
);
expect(response3.status).to.be(200);

View file

@ -262,44 +262,6 @@ export default function ({ getService }: FtrProviderContext) {
expect(response3.body.index_pattern.typeMeta).to.eql({ foo: 'baz' });
});
it('can update index_pattern fields', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
fields: {
foo: {
name: 'foo',
type: 'string',
},
},
},
});
expect(response1.body.index_pattern.fields.foo.name).to.be('foo');
expect(response1.body.index_pattern.fields.foo.type).to.be('string');
const id = response1.body.index_pattern.id;
const response2 = await supertest.post('/api/index_patterns/index_pattern/' + id).send({
index_pattern: {
fields: {
bar: {
name: 'bar',
type: 'number',
},
},
},
});
expect(response2.body.index_pattern.fields.bar.name).to.be('bar');
expect(response2.body.index_pattern.fields.bar.type).to.be('number');
const response3 = await supertest.get('/api/index_patterns/index_pattern/' + id);
expect(response3.body.index_pattern.fields.bar.name).to.be('bar');
expect(response3.body.index_pattern.fields.bar.type).to.be('number');
});
it('can update multiple index pattern fields at once', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({

View file

@ -21,9 +21,33 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
describe('errors', () => {
const basicIndex = 'b*sic_index';
let indexPattern: any;
before(async () => {
await esArchiver.load('index_patterns/basic_index');
indexPattern = (
await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title: basicIndex,
},
})
).body.index_pattern;
});
after(async () => {
await esArchiver.unload('index_patterns/basic_index');
if (indexPattern) {
await supertest.delete('/api/index_patterns/index_pattern/' + indexPattern.id);
}
});
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest.delete(
@ -34,35 +58,16 @@ export default function ({ getService }: FtrProviderContext) {
});
it('returns 404 error on non-existing scripted field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
},
});
const response2 = await supertest.delete(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/scripted_field/foo`
const response1 = await supertest.delete(
`/api/index_patterns/index_pattern/${indexPattern.id}/scripted_field/test`
);
expect(response2.status).to.be(404);
expect(response1.status).to.be(404);
});
it('returns error when attempting to delete a field which is not a scripted field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
fields: {
foo: {
scripted: false,
name: 'foo',
type: 'string',
},
},
},
});
const response2 = await supertest.delete(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/scripted_field/foo`
`/api/index_patterns/index_pattern/${indexPattern.id}/scripted_field/foo`
);
expect(response2.status).to.be(400);

View file

@ -21,9 +21,33 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../../ftr_provider_context';
export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const supertest = getService('supertest');
describe('errors', () => {
const basicIndex = '*asic_index';
let indexPattern: any;
before(async () => {
await esArchiver.load('index_patterns/basic_index');
indexPattern = (
await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title: basicIndex,
},
})
).body.index_pattern;
});
after(async () => {
await esArchiver.unload('index_patterns/basic_index');
if (indexPattern) {
await supertest.delete('/api/index_patterns/index_pattern/' + indexPattern.id);
}
});
it('returns 404 error on non-existing index_pattern', async () => {
const id = `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx-${Date.now()}`;
const response = await supertest.get(
@ -34,40 +58,11 @@ export default function ({ getService }: FtrProviderContext) {
});
it('returns 404 error on non-existing scripted field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
},
});
const response2 = await supertest.get(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/scripted_field/foo`
const response1 = await supertest.get(
`/api/index_patterns/index_pattern/${indexPattern.id}/scripted_field/sf`
);
expect(response2.status).to.be(404);
});
it('returns error when attempting to fetch a field which is not a scripted field', async () => {
const title = `foo-${Date.now()}-${Math.random()}*`;
const response1 = await supertest.post('/api/index_patterns/index_pattern').send({
index_pattern: {
title,
fields: {
foo: {
scripted: false,
name: 'foo',
type: 'string',
},
},
},
});
const response2 = await supertest.get(
`/api/index_patterns/index_pattern/${response1.body.index_pattern.id}/scripted_field/foo`
);
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be('Only scripted fields can be retrieved.');
expect(response1.status).to.be(404);
});
it('returns error when ID is too long', async () => {
@ -81,5 +76,22 @@ export default function ({ getService }: FtrProviderContext) {
'[request params.id]: value has length [1759] but it must have a maximum length of [1000].'
);
});
it('returns 404 error on non-existing scripted field', async () => {
const response1 = await supertest.get(
`/api/index_patterns/index_pattern/${indexPattern.id}/scripted_field/test`
);
expect(response1.status).to.be(404);
});
it('returns error when attempting to fetch a field which is not a scripted field', async () => {
const response2 = await supertest.get(
`/api/index_patterns/index_pattern/${indexPattern.id}/scripted_field/foo`
);
expect(response2.status).to.be(400);
expect(response2.body.statusCode).to.be(400);
expect(response2.body.message).to.be('Only scripted fields can be retrieved.');
});
});
}