Adds SOM integration tests for serverless (#184888)

fix of https://github.com/elastic/kibana/issues/175757

Adds serverless api integration tests for Saved Objects Management

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
Co-authored-by: Jean-Louis Leysens <jloleysens@gmail.com>
Co-authored-by: Jean-Louis Leysens <jeanlouis.leysens@elastic.co>
This commit is contained in:
Christiane (Tina) Heiligers 2024-06-13 06:31:57 -07:00 committed by GitHub
parent 5da44fb2f7
commit c2a82fe70d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 1199 additions and 2 deletions

View file

@ -342,7 +342,7 @@ export default function ({ getService }: FtrProviderContext) {
});
describe('index patterns', () => {
it('should validate visualization response schema', async () => {
it('should validate index-pattern response schema', async () => {
const resp = await supertest
.get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357'))
.expect(200);

View file

@ -0,0 +1,99 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { SavedObjectWithMetadata } from '@kbn/saved-objects-management-plugin/common';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { RoleCredentials } from '../../../../shared/services';
export default function ({ getService }: FtrProviderContext) {
const svlCommonApi = getService('svlCommonApi');
const svlUserManager = getService('svlUserManager');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const kibanaServer = getService('kibanaServer');
let roleAuthc: RoleCredentials;
describe('_bulk_delete', () => {
const endpoint = '/internal/kibana/management/saved_objects/_bulk_delete';
const validObject = { type: 'visualization', id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab' };
const invalidObject = { type: 'wigwags', id: 'foo' };
before(async () => {
roleAuthc = await svlUserManager.createApiKeyForRole('admin');
});
after(async () => {
await svlUserManager.invalidateApiKeyForRole(roleAuthc);
});
beforeEach(() =>
kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
)
);
afterEach(() =>
kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
)
);
function expectSuccess(index: number, objs: SavedObjectWithMetadata[]) {
const { type, id, error } = objs[index];
expect(type).to.eql(validObject.type);
expect(id).to.eql(validObject.id);
expect(error).to.equal(undefined);
}
function expectBadRequest(index: number, objs: SavedObjectWithMetadata[]) {
const { type, id, error } = objs[index];
expect(type).to.eql(invalidObject.type);
expect(id).to.eql(invalidObject.id);
expect(error).to.eql({
message: `Unsupported saved object type: '${invalidObject.type}': Bad Request`,
statusCode: 400,
error: 'Bad Request',
});
}
it('should return 200 for an existing object', async () =>
await supertestWithoutAuth
.post(endpoint)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send([validObject])
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expectSuccess(0, body);
}));
it('should return error for invalid object type', async () =>
await supertestWithoutAuth
.post(endpoint)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send([invalidObject])
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expectBadRequest(0, body);
}));
it('should return mix of successes and errors', async () =>
await supertestWithoutAuth
.post(endpoint)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send([validObject, invalidObject])
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(2);
expectSuccess(0, body);
expectBadRequest(1, body);
}));
});
}

View file

@ -0,0 +1,102 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { SavedObjectWithMetadata } from '@kbn/saved-objects-management-plugin/common';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { RoleCredentials } from '../../../../shared/services';
export default function ({ getService }: FtrProviderContext) {
const svlCommonApi = getService('svlCommonApi');
const svlUserManager = getService('svlUserManager');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const kibanaServer = getService('kibanaServer');
let roleAuthc: RoleCredentials;
const URL = '/api/kibana/management/saved_objects/_bulk_get';
const validObject = { type: 'visualization', id: 'dd7caf20-9efd-11e7-acb3-3dab96693fab' };
const invalidObject = { type: 'wigwags', id: 'foo' };
describe('_bulk_get', () => {
before(async () => {
roleAuthc = await svlUserManager.createApiKeyForRole('admin');
});
after(async () => {
await svlUserManager.invalidateApiKeyForRole(roleAuthc);
});
describe('get objects in bulk', () => {
before(() =>
kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
)
);
after(() =>
kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
)
);
function expectSuccess(index: number, objs: SavedObjectWithMetadata[]) {
const { type, id, meta, error } = objs[index];
expect(type).to.eql(validObject.type);
expect(id).to.eql(validObject.id);
expect(meta).to.not.equal(undefined);
expect(error).to.equal(undefined);
}
function expectBadRequest(index: number, objs: SavedObjectWithMetadata[]) {
const { type, id, error } = objs[index];
expect(type).to.eql(invalidObject.type);
expect(id).to.eql(invalidObject.id);
expect(error).to.eql({
message: `Unsupported saved object type: '${invalidObject.type}': Bad Request`,
statusCode: 400,
error: 'Bad Request',
});
}
it('should return 200 for object that exists and inject metadata', async () =>
await supertestWithoutAuth
.post(URL)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send([validObject])
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expectSuccess(0, body);
}));
it('should return error for invalid object type', async () =>
await supertestWithoutAuth
.post(URL)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send([invalidObject])
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(1);
expectBadRequest(0, body);
}));
it('should return mix of successes and errors', async () =>
await supertestWithoutAuth
.post(URL)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send([validObject, invalidObject])
.expect(200)
.then(({ body }) => {
expect(body).to.have.length(2);
expectSuccess(0, body);
expectBadRequest(1, body);
}));
});
});
}

View file

@ -0,0 +1,308 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { RoleCredentials } from '../../../../shared/services';
export default function ({ getService }: FtrProviderContext) {
const svlCommonApi = getService('svlCommonApi');
const svlUserManager = getService('svlUserManager');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const kibanaServer = getService('kibanaServer');
let roleAuthc: RoleCredentials;
describe('find', () => {
before(async () => {
roleAuthc = await svlUserManager.createApiKeyForRole('admin');
});
after(async () => {
await svlUserManager.invalidateApiKeyForRole(roleAuthc);
});
describe('with kibana index - basic', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
after(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
it('should return 200 with individual responses', async () => {
const { body } = await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find?type=visualization')
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(body.saved_objects.map((so: { id: string }) => so.id)).to.eql([
'dd7caf20-9efd-11e7-acb3-3dab96693fab',
]);
});
describe('unknown type', () => {
it('should return 200 with empty response', async () => {
const { body } = await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find?type=wigwags')
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(body).to.eql({
page: 1,
per_page: 20,
total: 0,
saved_objects: [],
});
});
});
describe('page beyond total', () => {
it('should return 200 with empty response', async () => {
const { body } = await supertestWithoutAuth
.get(
'/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100'
)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(body).to.eql({
page: 100,
per_page: 100,
total: 1,
saved_objects: [],
});
});
});
describe('unknown search field', () => {
it('should return 400 when using searchFields', async () => {
const { body } = await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find?type=url&searchFields=a')
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(400);
expect(body).to.eql({
statusCode: 400,
error: 'Bad Request',
message: '[request query.searchFields]: definition for this key is missing',
});
});
});
});
describe('with kibana index - relationships', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/references.json'
);
});
after(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/references.json'
);
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/basic.json'
);
});
describe('`hasReference` and `hasReferenceOperator` parameters', () => {
it('search for a reference', async () => {
const { body } = await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find')
.query({
type: 'visualization',
hasReference: JSON.stringify({ type: 'ref-type', id: 'ref-1' }),
})
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
const objects = body.saved_objects;
expect(objects.map((obj: any) => obj.id)).to.eql(['only-ref-1', 'ref-1-and-ref-2']);
});
});
it('search for multiple references with OR operator', async () => {
await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find')
.query({
type: 'visualization',
hasReference: JSON.stringify([
{ type: 'ref-type', id: 'ref-1' },
{ type: 'ref-type', id: 'ref-2' },
]),
hasReferenceOperator: 'OR',
})
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.then((response) => {
expect(response.status).to.eql(200);
expect(response.body.saved_objects.length).not.to.be(null);
expect(response.body.saved_objects.map((obj: any) => obj.id).length).to.be.greaterThan(
0
);
});
});
it('search for multiple references with AND operator', async () => {
const { body } = await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find')
.query({
type: 'visualization',
hasReference: JSON.stringify([
{ type: 'ref-type', id: 'ref-1' },
{ type: 'ref-type', id: 'ref-2' },
]),
hasReferenceOperator: 'AND',
})
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
const objects = body.saved_objects;
expect(objects.map((obj: any) => obj.id)).to.eql(['ref-1-and-ref-2']);
});
describe('`sortField` and `sortOrder` parameters', () => {
it('sort objects by "type" in "asc" order', async () => {
const { body } = await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find')
.query({
type: ['visualization', 'dashboard'],
sortField: 'type',
sortOrder: 'asc',
})
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
const objects = body.saved_objects;
expect(objects.length).be.greaterThan(1); // Need more than 1 result for our test
expect(objects[0].type).to.be('dashboard');
});
// does not work in serverless mode
it('sort objects by "type" in "desc" order', async () => {
const { body } = await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find')
.query({
type: ['visualization', 'dashboard'],
sortField: 'type',
sortOrder: 'desc',
})
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
const objects = body.saved_objects;
expect(objects[0].type).to.be('visualization');
});
});
});
describe('meta attributes injected properly', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/search.json'
);
});
after(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/search.json'
);
await kibanaServer.savedObjects.cleanStandardList();
});
it('should inject meta attributes for searches', async () =>
await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find?type=search')
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200)
.then((response) => {
expect(response.body.saved_objects).to.have.length(1);
expect(response.body.saved_objects[0].meta).to.eql({
icon: 'discoverApp',
title: 'OneRecord',
hiddenType: false,
inAppUrl: {
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'discover.show',
},
namespaceType: 'multiple-isolated',
});
}));
it('should inject meta attributes for dashboards', async () =>
await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find?type=dashboard')
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200)
.then((response) => {
expect(response.body.saved_objects).to.have.length(1);
expect(response.body.saved_objects[0].meta).to.eql({
icon: 'dashboardApp',
title: 'Dashboard',
hiddenType: false,
inAppUrl: {
path: '/app/dashboards#/view/b70c7ae0-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'dashboard.show',
},
namespaceType: 'multiple-isolated',
});
}));
it('should inject meta attributes for visualizations', async () =>
await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find?type=visualization')
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200)
.then((response) => {
expect(response.body.saved_objects).to.have.length(2);
expect(response.body.saved_objects[0].meta).to.eql({
icon: 'visualizeApp',
title: 'VisualizationFromSavedSearch',
namespaceType: 'multiple-isolated',
hiddenType: false,
});
expect(response.body.saved_objects[1].meta).to.eql({
icon: 'visualizeApp',
title: 'Visualization',
namespaceType: 'multiple-isolated',
hiddenType: false,
});
}));
it('should inject meta attributes for index patterns', async () =>
await supertestWithoutAuth
.get('/api/kibana/management/saved_objects/_find?type=index-pattern')
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200)
.then((response) => {
expect(response.body.saved_objects).to.have.length(1);
expect(response.body.saved_objects[0].meta).to.eql({
icon: 'indexPatternApp',
title: 'saved_objects*',
hiddenType: false,
editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
inAppUrl: {
path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
namespaceType: 'multiple',
});
}));
});
});
}

View file

@ -0,0 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('saved objects management apis', () => {
loadTestFile(require.resolve('./find'));
loadTestFile(require.resolve('./bulk_get'));
loadTestFile(require.resolve('./bulk_delete'));
loadTestFile(require.resolve('./scroll_count'));
loadTestFile(require.resolve('./relationships'));
});
}

View file

@ -0,0 +1,473 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { schema } from '@kbn/config-schema';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { RoleCredentials } from '../../../../shared/services';
export default function ({ getService }: FtrProviderContext) {
const svlCommonApi = getService('svlCommonApi');
const svlUserManager = getService('svlUserManager');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const kibanaServer = getService('kibanaServer');
let roleAuthc: RoleCredentials;
const relationSchema = schema.object({
id: schema.string(),
type: schema.string(),
relationship: schema.oneOf([schema.literal('parent'), schema.literal('child')]),
meta: schema.object({
title: schema.string(),
icon: schema.string(),
editUrl: schema.maybe(schema.string()),
// visualizations don't declare an inAppUrl
inAppUrl: schema.maybe(
schema.object({
path: schema.string(),
uiCapabilitiesPath: schema.string(),
})
),
namespaceType: schema.string(),
hiddenType: schema.boolean(),
}),
});
const invalidRelationSchema = schema.object({
id: schema.string(),
type: schema.string(),
relationship: schema.oneOf([schema.literal('parent'), schema.literal('child')]),
error: schema.string(),
});
const responseSchema = schema.object({
relations: schema.arrayOf(relationSchema),
invalidRelations: schema.arrayOf(invalidRelationSchema),
});
describe('relationships', () => {
before(async () => {
roleAuthc = await svlUserManager.createApiKeyForRole('admin');
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json'
);
});
after(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/management/saved_objects/relationships.json'
);
await kibanaServer.savedObjects.cleanStandardList();
await svlUserManager.invalidateApiKeyForRole(roleAuthc);
});
const baseApiUrl = `/api/kibana/management/saved_objects/relationships`;
const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard'];
const relationshipsUrl = (type: string, id: string, types: string[] = defaultTypes) => {
const typesQuery = types.map((t) => `savedObjectTypes=${t}`).join('&');
return `${baseApiUrl}/${type}/${id}?${typesQuery}`;
};
describe('validate response schema', () => {
it('search', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(() => {
responseSchema.validate(resp.body);
}).not.to.throwError();
});
it('dashboard', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(() => {
responseSchema.validate(resp.body);
}).not.to.throwError();
});
it('visualization', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(() => {
responseSchema.validate(resp.body);
}).not.to.throwError();
});
it('index-pattern', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(() => {
responseSchema.validate(resp.body);
}).not.to.throwError();
});
it('invalid-refs', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('dashboard', 'invalid-refs'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(() => {
responseSchema.validate(resp.body);
}).not.to.throwError();
});
});
describe('should work', () => {
it('for searches', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: '8963ca30-3224-11e8-a572-ffca06da1357',
type: 'index-pattern',
relationship: 'child',
meta: {
title: 'saved_objects*',
icon: 'indexPatternApp',
editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
inAppUrl: {
path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
namespaceType: 'multiple',
hiddenType: false,
},
},
{
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
type: 'visualization',
relationship: 'parent',
meta: {
title: 'VisualizationFromSavedSearch',
icon: 'visualizeApp',
namespaceType: 'multiple-isolated',
hiddenType: false,
},
},
]);
});
it('for dashboards', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: 'add810b0-3224-11e8-a572-ffca06da1357',
type: 'visualization',
relationship: 'child',
meta: {
icon: 'visualizeApp',
title: 'Visualization',
namespaceType: 'multiple-isolated',
hiddenType: false,
},
},
{
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
type: 'visualization',
relationship: 'child',
meta: {
icon: 'visualizeApp',
title: 'VisualizationFromSavedSearch',
namespaceType: 'multiple-isolated',
hiddenType: false,
},
},
]);
});
it('for visualizations', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: '960372e0-3224-11e8-a572-ffca06da1357',
type: 'search',
relationship: 'child',
meta: {
icon: 'discoverApp',
title: 'OneRecord',
inAppUrl: {
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'discover.show',
},
namespaceType: 'multiple-isolated',
hiddenType: false,
},
},
{
id: 'b70c7ae0-3224-11e8-a572-ffca06da1357',
type: 'dashboard',
relationship: 'parent',
meta: {
icon: 'dashboardApp',
title: 'Dashboard',
inAppUrl: {
path: '/app/dashboards#/view/b70c7ae0-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'dashboard.show',
},
namespaceType: 'multiple-isolated',
hiddenType: false,
},
},
]);
});
it('for index patterns', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: '960372e0-3224-11e8-a572-ffca06da1357',
type: 'search',
relationship: 'parent',
meta: {
icon: 'discoverApp',
title: 'OneRecord',
inAppUrl: {
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'discover.show',
},
namespaceType: 'multiple-isolated',
hiddenType: false,
},
},
{
id: 'add810b0-3224-11e8-a572-ffca06da1357',
type: 'visualization',
relationship: 'parent',
meta: {
icon: 'visualizeApp',
title: 'Visualization',
namespaceType: 'multiple-isolated',
hiddenType: false,
},
},
]);
});
});
describe('should filter based on savedObjectTypes', () => {
it('search', async () => {
const resp = await supertestWithoutAuth
.get(
relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization'])
)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: '8963ca30-3224-11e8-a572-ffca06da1357',
type: 'index-pattern',
meta: {
icon: 'indexPatternApp',
title: 'saved_objects*',
editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
inAppUrl: {
path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'management.kibana.indexPatterns',
},
namespaceType: 'multiple',
hiddenType: false,
},
relationship: 'child',
},
{
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
type: 'visualization',
meta: {
icon: 'visualizeApp',
title: 'VisualizationFromSavedSearch',
namespaceType: 'multiple-isolated',
hiddenType: false,
},
relationship: 'parent',
},
]);
});
it('dashboard', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search']))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: 'add810b0-3224-11e8-a572-ffca06da1357',
type: 'visualization',
meta: {
icon: 'visualizeApp',
title: 'Visualization',
namespaceType: 'multiple-isolated',
hiddenType: false,
},
relationship: 'child',
},
{
id: 'a42c0580-3224-11e8-a572-ffca06da1357',
type: 'visualization',
meta: {
icon: 'visualizeApp',
title: 'VisualizationFromSavedSearch',
namespaceType: 'multiple-isolated',
hiddenType: false,
},
relationship: 'child',
},
]);
});
it('visualization', async () => {
const resp = await supertestWithoutAuth
.get(
relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search'])
)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: '960372e0-3224-11e8-a572-ffca06da1357',
type: 'search',
meta: {
icon: 'discoverApp',
title: 'OneRecord',
inAppUrl: {
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'discover.show',
},
namespaceType: 'multiple-isolated',
hiddenType: false,
},
relationship: 'child',
},
]);
});
it('index-pattern', async () => {
const resp = await supertestWithoutAuth
.get(
relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search'])
)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body.relations).to.eql([
{
id: '960372e0-3224-11e8-a572-ffca06da1357',
type: 'search',
meta: {
icon: 'discoverApp',
title: 'OneRecord',
inAppUrl: {
path: '/app/discover#/view/960372e0-3224-11e8-a572-ffca06da1357',
uiCapabilitiesPath: 'discover.show',
},
namespaceType: 'multiple-isolated',
hiddenType: false,
},
relationship: 'parent',
},
]);
});
});
describe('should return 404 no results for', () => {
it('a search', async () => {
await supertestWithoutAuth
.get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(404);
});
it('a dashboard', async () => {
await supertestWithoutAuth
.get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(404);
});
it('a visualization', async () => {
await supertestWithoutAuth
.get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(404);
});
it('an index pattern', async () => {
await supertestWithoutAuth
.get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(404);
});
});
describe('invalid references', () => {
it('should return the invalid relations', async () => {
const resp = await supertestWithoutAuth
.get(relationshipsUrl('dashboard', 'invalid-refs'))
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.expect(200);
expect(resp.body).to.eql({
invalidRelations: [
{
error: 'Saved object [visualization/invalid-vis] not found',
id: 'invalid-vis',
relationship: 'child',
type: 'visualization',
},
],
relations: [
{
id: 'add810b0-3224-11e8-a572-ffca06da1357',
meta: {
icon: 'visualizeApp',
namespaceType: 'multiple-isolated',
hiddenType: false,
title: 'Visualization',
},
relationship: 'child',
type: 'visualization',
},
],
});
});
});
});
}

View file

@ -0,0 +1,193 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { RoleCredentials } from '../../../../shared/services';
const apiUrl = '/api/kibana/management/saved_objects/scroll/counts';
const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard'];
export default function ({ getService }: FtrProviderContext) {
const svlCommonApi = getService('svlCommonApi');
const svlUserManager = getService('svlUserManager');
const supertestWithoutAuth = getService('supertestWithoutAuth');
const kibanaServer = getService('kibanaServer');
let roleAuthc: RoleCredentials;
describe('scroll_count', () => {
before(async () => {
roleAuthc = await svlUserManager.createApiKeyForRole('admin');
});
after(async () => {
await svlUserManager.invalidateApiKeyForRole(roleAuthc);
});
describe('scroll_count with more than 10k objects', () => {
const importVisualizations = async ({
startIdx = 1,
endIdx,
}: {
startIdx?: number;
endIdx: number;
}) => {
const fileChunks: string[] = [];
for (let i = startIdx; i <= endIdx; i++) {
const id = `test-vis-${i}`;
fileChunks.push(
JSON.stringify({
type: 'visualization',
id,
attributes: {
title: `My visualization (${i})`,
uiStateJSON: '{}',
visState: '{}',
},
references: [],
})
);
}
await supertestWithoutAuth
.post(`/api/saved_objects/_import`)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson')
.expect(200);
};
const deleteVisualizations = async ({
startIdx = 1,
endIdx,
}: {
startIdx?: number;
endIdx: number;
}) => {
const objsToDelete: any[] = [];
for (let i = startIdx; i <= endIdx; i++) {
const id = `test-vis-${i}`;
objsToDelete.push({ type: 'visualization', id });
}
await kibanaServer.savedObjects.bulkDelete({ objects: objsToDelete });
};
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await importVisualizations({ startIdx: 1, endIdx: 6000 });
await importVisualizations({ startIdx: 6001, endIdx: 12000 });
});
after(async () => {
// kibanaServer.savedObjects.cleanStandardList({}); times out for 12000 items
await deleteVisualizations({ startIdx: 1, endIdx: 3000 });
await deleteVisualizations({ startIdx: 3001, endIdx: 6000 });
await deleteVisualizations({ startIdx: 6001, endIdx: 9000 });
await deleteVisualizations({ startIdx: 9001, endIdx: 12000 });
});
it('returns the correct count for each included types', async () => {
const { body } = await supertestWithoutAuth
.post(apiUrl)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send({
typesToInclude: ['visualization'],
})
.expect(200);
expect(body).to.eql({
visualization: 12000,
});
});
});
describe('with less than 10k objects', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
await kibanaServer.importExport.load(
'test/api_integration/fixtures/kbn_archiver/saved_objects/scroll_count.json'
);
});
after(async () => {
await kibanaServer.importExport.unload(
'test/api_integration/fixtures/kbn_archiver/saved_objects/scroll_count.json'
);
await kibanaServer.savedObjects.cleanStandardList();
});
it('returns the count for each included types', async () => {
const { body } = await supertestWithoutAuth
.post(apiUrl)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send({
typesToInclude: defaultTypes,
})
.expect(200);
expect(body).to.eql({
dashboard: 2,
'index-pattern': 1,
search: 1,
visualization: 2,
});
});
it('only returns count for types to include', async () => {
const { body } = await supertestWithoutAuth
.post(apiUrl)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send({
typesToInclude: ['dashboard', 'search'],
})
.expect(200);
expect(body).to.eql({
dashboard: 2,
search: 1,
});
});
it('filters on title when `searchString` is provided', async () => {
const { body } = await supertestWithoutAuth
.post(apiUrl)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send({
typesToInclude: defaultTypes,
searchString: 'Amazing',
})
.expect(200);
expect(body).to.eql({
dashboard: 1,
visualization: 1,
'index-pattern': 0,
search: 0,
});
});
it('includes all requested types even when none match the search', async () => {
const { body } = await supertestWithoutAuth
.post(apiUrl)
.set(svlCommonApi.getInternalRequestHeader())
.set(roleAuthc.apiKeyHeader)
.send({
typesToInclude: ['dashboard', 'search', 'visualization'],
searchString: 'nothing-will-match',
})
.expect(200);
expect(body).to.eql({
dashboard: 0,
visualization: 0,
search: 0,
});
});
});
});
}

View file

@ -30,6 +30,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
require.resolve('../../common/grok_debugger'),
require.resolve('../../common/painless_lab'),
require.resolve('../../common/console'),
require.resolve('../../common/saved_objects_management'),
require.resolve('../../common/telemetry'),
],
junit: {

View file

@ -28,6 +28,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
require.resolve('../../common/core'),
require.resolve('../../common/reporting'),
require.resolve('../../common/console'),
require.resolve('../../common/saved_objects_management'),
require.resolve('../../common/telemetry'),
],
junit: {

View file

@ -30,6 +30,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
require.resolve('../../common/grok_debugger'),
require.resolve('../../common/painless_lab'),
require.resolve('../../common/console'),
require.resolve('../../common/saved_objects_management'),
require.resolve('../../common/telemetry'),
],
junit: {

View file

@ -102,6 +102,7 @@
"@kbn/alerting-comparators",
"@kbn/search-types",
"@kbn/reporting-server",
"@kbn/features-plugin"
"@kbn/config-schema",
"@kbn/features-plugin",
]
}