Adds SOM ftr functional tests to serverless to run in simulated env (#174976)

fix https://github.com/elastic/kibana/issues/174545
Adds SOM tests to serverless

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Christiane (Tina) Heiligers 2024-01-22 07:50:25 -07:00 committed by GitHub
parent 44e987271f
commit 6002f14481
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 1301 additions and 2 deletions

View file

@ -415,6 +415,7 @@ enabled:
- x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts
- x-pack/test_serverless/functional/test_suites/observability/config.ts
- x-pack/test_serverless/functional/test_suites/observability/config.examples.ts
- x-pack/test_serverless/functional/test_suites/observability/config.saved_objects_management.ts
- x-pack/test_serverless/functional/test_suites/observability/common_configs/config.group1.ts
- x-pack/test_serverless/functional/test_suites/observability/common_configs/config.group2.ts
- x-pack/test_serverless/functional/test_suites/observability/common_configs/config.group3.ts
@ -426,6 +427,7 @@ enabled:
- x-pack/test_serverless/functional/test_suites/search/config.ts
- x-pack/test_serverless/functional/test_suites/search/config.examples.ts
- x-pack/test_serverless/functional/test_suites/search/config.screenshots.ts
- x-pack/test_serverless/functional/test_suites/search/config.saved_objects_management.ts
- x-pack/test_serverless/functional/test_suites/search/common_configs/config.group1.ts
- x-pack/test_serverless/functional/test_suites/search/common_configs/config.group2.ts
- x-pack/test_serverless/functional/test_suites/search/common_configs/config.group3.ts
@ -435,6 +437,7 @@ enabled:
- x-pack/test_serverless/functional/test_suites/security/config.ts
- x-pack/test_serverless/functional/test_suites/security/config.examples.ts
- x-pack/test_serverless/functional/test_suites/security/config.cloud_security_posture.ts
- x-pack/test_serverless/functional/test_suites/security/config.saved_objects_management.ts
- x-pack/test_serverless/functional/test_suites/security/common_configs/config.group1.ts
- x-pack/test_serverless/functional/test_suites/security/common_configs/config.group2.ts
- x-pack/test_serverless/functional/test_suites/security/common_configs/config.group3.ts
@ -516,4 +519,4 @@ enabled:
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_read/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_read/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_bulk_actions/configs/ess.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_bulk_actions/configs/serverless.config.ts
- x-pack/test/security_solution_api_integration/test_suites/detections_response/default_license/rule_bulk_actions/configs/serverless.config.ts

View file

@ -18,7 +18,7 @@
"api_integration/apis/logstash/pipelines/fixtures/*.json",
"api_integration/apis/telemetry/fixtures/*.json",
"api_integration/apis/telemetry/fixtures/*.json"
],
, "../x-pack/test_serverless/functional/test_suites/common/saved_objects_management/export_transform copy.ts" ],
"exclude": ["target/**/*", "*/plugins/**/*", "plugins/**/*"],
"kbn_references": [
"@kbn/core",

View file

@ -0,0 +1,12 @@
{
"attributes": {
"enabled": true,
"title": "vim-1"
},
"coreMigrationVersion": "7.14.0",
"id": "test-not-visible-in-management:vim-1",
"references": [],
"type": "test-not-visible-in-management",
"updated_at": "2018-12-21T00:43:07.096Z",
"version": "WzIsMV0="
}

View file

@ -0,0 +1,113 @@
/*
* 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 type { Response } from 'supertest';
import type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const svlCommonApi = getService('svlCommonApi');
const testSubjects = getService('testSubjects');
describe('_bulk_get', () => {
describe('saved objects with hidden type', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects'
);
await kibanaServer.importExport.load(
'x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/hidden_saved_objects'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects'
);
await kibanaServer.importExport.unload(
'x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/hidden_saved_objects'
);
await kibanaServer.savedObjects.cleanStandardList();
await pageObjects.svlCommonPage.forceLogout();
});
const URL = '/api/kibana/management/saved_objects/_bulk_get';
const hiddenTypeExportableImportable = {
type: 'test-hidden-importable-exportable',
id: 'ff3733a0-9fty-11e7-ahb3-3dcb94193fab',
};
const hiddenTypeNonExportableImportable = {
type: 'test-hidden-non-importable-exportable',
id: 'op3767a1-9rcg-53u7-jkb3-3dnb74193awc',
};
function expectSuccess(index: number, { body }: Response) {
const { type, id, meta, error } = body[index];
expect(type).to.eql(hiddenTypeExportableImportable.type);
expect(id).to.eql(hiddenTypeExportableImportable.id);
expect(meta).to.not.equal(undefined);
expect(error).to.equal(undefined);
}
function expectBadRequest(index: number, { body }: Response) {
const { type, id, error } = body[index];
expect(type).to.eql(hiddenTypeNonExportableImportable.type);
expect(id).to.eql(hiddenTypeNonExportableImportable.id);
expect(error).to.eql({
message: `Unsupported saved object type: '${hiddenTypeNonExportableImportable.type}': Bad Request`,
statusCode: 400,
error: 'Bad Request',
});
}
it('should return 200 for hidden types that are importableAndExportable', async () =>
await supertest
.post(URL)
.send([hiddenTypeExportableImportable])
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response: Response) => {
expect(response.body).to.have.length(1);
expectSuccess(0, response);
}));
it('should return error for hidden types that are not importableAndExportable', async () =>
await supertest
.post(URL)
.send([hiddenTypeNonExportableImportable])
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response: Response) => {
expect(response.body).to.have.length(1);
expectBadRequest(0, response);
}));
it('should return mix of successes and errors', async () =>
await supertest
.post(URL)
.send([hiddenTypeExportableImportable, hiddenTypeNonExportableImportable])
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response: Response) => {
expect(response.body).to.have.length(2);
expectSuccess(0, response);
expectBadRequest(1, response);
}));
});
});
}

View file

@ -0,0 +1,334 @@
/*
* 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 type { SavedObject } from '@kbn/core/types';
import type { SavedObjectsExportResultDetails } from '@kbn/core/server';
import { FtrProviderContext } from '../../../ftr_provider_context';
function parseNdJson(input: string): Array<SavedObject<any>> {
return input.split('\n').map((str) => JSON.parse(str));
}
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const svlCommonApi = getService('svlCommonApi');
const testSubjects = getService('testSubjects');
describe('export transforms', () => {
describe('root objects export transforms', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/export_transform'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/export_transform'
);
await kibanaServer.savedObjects.cleanStandardList();
await pageObjects.svlCommonPage.forceLogout();
});
it('allows to mutate the objects during an export', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
type: ['test-export-transform'],
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.map((obj) => ({ id: obj.id, enabled: obj.attributes.enabled }))).to.eql([
{
id: 'type_1-obj_1',
enabled: false,
},
{
id: 'type_1-obj_2',
enabled: false,
},
]);
});
});
it('allows to add additional objects to an export', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
objects: [
{
type: 'test-export-add',
id: 'type_2-obj_1',
},
],
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.map((obj) => obj.id)).to.eql(['type_2-obj_1', 'type_dep-obj_1']);
});
});
it('allows to add additional objects to an export when exporting by type', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
type: ['test-export-add'],
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.map((obj) => obj.id)).to.eql([
'type_2-obj_1',
'type_2-obj_2',
'type_dep-obj_1',
'type_dep-obj_2',
]);
});
});
it('returns a 400 when the type causes a transform error', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
type: ['test-export-transform-error'],
excludeExportDetails: true,
})
.expect(400)
.then((resp) => {
const { attributes, ...error } = resp.body;
expect(error).to.eql({
error: 'Bad Request',
message: 'Error transforming objects to export',
statusCode: 400,
});
expect(attributes.cause).to.eql('Error during transform');
expect(attributes.objects.map((obj: any) => obj.id)).to.eql(['type_4-obj_1']);
});
});
it('returns a 400 when the type causes an invalid transform', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
type: ['test-export-invalid-transform'],
excludeExportDetails: true,
})
.expect(400)
.then((resp) => {
expect(resp.body).to.eql({
error: 'Bad Request',
message: 'Invalid transform performed on objects to export',
statusCode: 400,
attributes: {
objectKeys: ['test-export-invalid-transform|type_3-obj_1'],
},
});
});
});
});
describe('nested export transforms', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/nested_export_transform'
);
await kibanaServer.savedObjects.cleanStandardList();
await pageObjects.svlCommonPage.forceLogout();
});
it('execute export transforms for reference objects', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
objects: [
{
type: 'test-export-transform',
id: 'type_1-obj_1',
},
],
includeReferencesDeep: true,
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text).sort((obj1, obj2) =>
obj1.id.localeCompare(obj2.id)
);
expect(objects.map((obj) => obj.id)).to.eql([
'type_1-obj_1',
'type_1-obj_2',
'type_2-obj_1',
'type_dep-obj_1',
]);
expect(objects[0].attributes.enabled).to.eql(false);
expect(objects[1].attributes.enabled).to.eql(false);
});
});
});
describe('isExportable API', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/export_exclusion'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/export_exclusion'
);
await kibanaServer.savedObjects.cleanStandardList();
await pageObjects.svlCommonPage.forceLogout();
});
it('should only export objects returning `true` for `isExportable`', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
objects: [
{
type: 'test-is-exportable',
id: '1',
},
],
includeReferencesDeep: true,
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text).sort((obj1, obj2) =>
obj1.id.localeCompare(obj2.id)
);
expect(objects.map((obj) => `${obj.type}:${obj.id}`)).to.eql([
'test-is-exportable:1',
'test-is-exportable:3',
'test-is-exportable:5',
]);
});
});
it('lists objects that got filtered', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
objects: [
{
type: 'test-is-exportable',
id: '1',
},
],
includeReferencesDeep: true,
excludeExportDetails: false,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
const exportDetails = objects[
objects.length - 1
] as unknown as SavedObjectsExportResultDetails;
expect(exportDetails.excludedObjectsCount).to.eql(2);
expect(exportDetails.excludedObjects).to.eql([
{
type: 'test-is-exportable',
id: '2',
reason: 'excluded',
},
{
type: 'test-is-exportable',
id: '4',
reason: 'excluded',
},
]);
});
});
it('excludes objects if `isExportable` throws', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
objects: [
{
type: 'test-is-exportable',
id: '5',
},
{
type: 'test-is-exportable',
id: 'error',
},
],
includeReferencesDeep: true,
excludeExportDetails: false,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.length).to.eql(2);
expect([objects[0]].map((obj) => `${obj.type}:${obj.id}`)).to.eql([
'test-is-exportable:5',
]);
const exportDetails = objects[
objects.length - 1
] as unknown as SavedObjectsExportResultDetails;
expect(exportDetails.excludedObjects).to.eql([
{
type: 'test-is-exportable',
id: 'error',
reason: 'predicate_error',
},
]);
});
});
});
});
}

View file

@ -0,0 +1,2 @@
{"attributes":{"title": "Test Import warnings 1"},"id":"08ff1d6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_1","version":1}
{"attributes":{"title": "Test Import warnings 2"},"id":"77bb1e6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_2","version":1}

View file

@ -0,0 +1,2 @@
{"attributes": {"title": "I am hidden from http apis but the client can still see me"},"id": "hidden-from-http-apis-import1","references": [],"type":"test-hidden-from-http-apis-importable-exportable","version": 1}
{"attributes": {"title": "I am not hidden from http apis"},"id": "not-hidden-from-http-apis-import1","references": [],"type": "test-not-hidden-from-http-apis-importable-exportable","version": 1}

View file

@ -0,0 +1 @@
{"attributes": { "title": "Hidden Saved object type that is importable/exportable." }, "id":"ff3733a0-9fty-11e7-ahb3-3dcb94193fab", "references":[], "type":"test-hidden-importable-exportable", "version":1}

View file

@ -0,0 +1 @@
{"attributes": { "title": "Hidden Saved object type that is not importable/exportable." },"id":"op3767a1-9rcg-53u7-jkb3-3dnb74193awc","references":[],"type":"test-hidden-non-importable-exportable","version":1}

View file

@ -0,0 +1 @@
{"attributes": { "title": "Saved object type that is not visible in management" }, "id":"ff3773b0-9ate-11e7-ahb3-3dcb94193fab", "references":[], "type":"test-not-visible-in-management", "version":1}

View file

@ -0,0 +1 @@
{"attributes":{"title": "Test Import warnings 1"},"id":"08ff1d6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_1","version":1}

View file

@ -0,0 +1 @@
{"attributes":{"title": "Test Import warnings 2"},"id":"77bb1e6a-a2e7-11e7-bb30-2e3be9be6a73","migrationVersion":{"visualization":"7.0.0"},"references":[],"type":"test_import_warning_2","version":1}

View file

@ -0,0 +1,81 @@
/*
* 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';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const svlCommonApi = getService('svlCommonApi');
const testSubjects = getService('testSubjects');
describe('find', () => {
describe('saved objects with hidden type', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects'
);
await kibanaServer.importExport.load(
'x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/hidden_saved_objects'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects'
);
await kibanaServer.importExport.unload(
'x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/hidden_saved_objects'
);
// emptyKibanaIndex fails in Serverless with
// "index_not_found_exception: no such index [.kibana_ingest]",
// so it was switched to `savedObjects.cleanStandardList()
await kibanaServer.savedObjects.cleanStandardList();
await pageObjects.svlCommonPage.forceLogout();
});
it('returns saved objects with importableAndExportable types', async () =>
await supertest
.get('/api/kibana/management/saved_objects/_find?type=test-hidden-importable-exportable')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((resp) => {
expect(
resp.body.saved_objects.map((so: { id: string; type: string }) => ({
id: so.id,
type: so.type,
}))
).to.eql([
{
type: 'test-hidden-importable-exportable',
id: 'ff3733a0-9fty-11e7-ahb3-3dcb94193fab',
},
]);
}));
it('returns empty response for non importableAndExportable types', async () =>
await supertest
.get(
'/api/kibana/management/saved_objects/_find?type=test-hidden-non-importable-exportable'
)
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((resp) => {
expect(resp.body.saved_objects).to.eql([]);
}));
});
});
}

View file

@ -0,0 +1,219 @@
/*
* 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 { join } from 'path';
import expect from '@kbn/expect';
import type { Response } from 'supertest';
import { SavedObject } from '@kbn/core/types';
import { FtrProviderContext } from '../../../ftr_provider_context';
function parseNdJson(input: string): Array<SavedObject<any>> {
return input.split('\n').map((str) => JSON.parse(str));
}
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const supertest = getService('supertest');
const kbnServer = getService('kibanaServer');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
const testSubjects = getService('testSubjects');
describe('types with `hiddenFromHttpApis` ', () => {
before(async () => {
await kbnServer.savedObjects.cleanStandardList();
await kbnServer.importExport.load(
'test/functional/fixtures/kbn_archiver/saved_objects_management/hidden_from_http_apis'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
// We cannot use `kbnServer.importExport.unload` to clean up test fixtures.
// `kbnServer.importExport.unload` uses the global SOM `delete` HTTP API
// and will throw on `hiddenFromHttpApis:true` objects
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_from_http_apis'
);
await pageObjects.svlCommonPage.forceLogout();
});
describe('APIS', () => {
const hiddenFromHttpApisType = {
type: 'test-hidden-from-http-apis-importable-exportable',
id: 'hidden-from-http-apis-1',
};
const notHiddenFromHttpApisType = {
type: 'test-not-hidden-from-http-apis-importable-exportable',
id: 'not-hidden-from-http-apis-1',
};
describe('_bulk_get', () => {
describe('saved objects with hiddenFromHttpApis type', () => {
const URL = '/api/kibana/management/saved_objects/_bulk_get';
it('should return 200 for types that are not hidden from the http apis', async () =>
await supertest
.post(URL)
.send([notHiddenFromHttpApisType])
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response: Response) => {
expect(response.body).to.have.length(1);
const { type, id, meta, error } = response.body[0];
expect(type).to.eql(notHiddenFromHttpApisType.type);
expect(id).to.eql(notHiddenFromHttpApisType.id);
expect(meta).to.not.equal(undefined);
expect(error).to.equal(undefined);
}));
it('should return 200 for types that are hidden from the http apis', async () =>
await supertest
.post(URL)
.send([hiddenFromHttpApisType])
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response: Response) => {
expect(response.body).to.have.length(1);
const { type, id, meta, error } = response.body[0];
expect(type).to.eql(hiddenFromHttpApisType.type);
expect(id).to.eql(hiddenFromHttpApisType.id);
expect(meta).to.not.equal(undefined);
expect(error).to.equal(undefined);
}));
it('should return 200 for a mix of types', async () =>
await supertest
.post(URL)
.send([hiddenFromHttpApisType, notHiddenFromHttpApisType])
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.expect(200)
.then((response: Response) => {
expect(response.body).to.have.length(2);
const { type, id, meta, error } = response.body[0];
expect(type).to.eql(hiddenFromHttpApisType.type);
expect(id).to.eql(hiddenFromHttpApisType.id);
expect(meta).to.not.equal(undefined);
expect(error).to.equal(undefined);
}));
});
});
describe('find', () => {
it('returns saved objects registered as hidden from the http Apis', async () => {
await supertest
.get(`/api/kibana/management/saved_objects/_find?type=${hiddenFromHttpApisType.type}`)
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((resp) => {
expect(
resp.body.saved_objects.map((so: { id: string; type: string }) => ({
id: so.id,
type: so.type,
}))
).to.eql([
{
id: 'hidden-from-http-apis-1',
type: 'test-hidden-from-http-apis-importable-exportable',
},
{
id: 'hidden-from-http-apis-2',
type: 'test-hidden-from-http-apis-importable-exportable',
},
]);
});
});
});
describe('export', () => {
it('allows to export them directly by id', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
objects: [
{
type: 'test-hidden-from-http-apis-importable-exportable',
id: 'hidden-from-http-apis-1',
},
],
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.map((obj) => obj.id)).to.eql(['hidden-from-http-apis-1']);
});
});
it('allows to export them directly by type', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
type: ['test-hidden-from-http-apis-importable-exportable'],
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.map((obj) => obj.id)).to.eql([
'hidden-from-http-apis-1',
'hidden-from-http-apis-2',
]);
});
});
});
describe('import', () => {
it('allows to import them', async () => {
await supertest
.post('/api/saved_objects/_import')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.attach('file', join(__dirname, './exports/_import_hidden_from_http_apis.ndjson'))
.expect(200)
.then((resp) => {
expect(resp.body).to.eql({
success: true,
successCount: 2,
successResults: [
{
id: 'hidden-from-http-apis-import1',
meta: {
title: 'I am hidden from http apis but the client can still see me',
},
type: 'test-hidden-from-http-apis-importable-exportable',
managed: false,
},
{
id: 'not-hidden-from-http-apis-import1',
meta: {
title: 'I am not hidden from http apis',
},
type: 'test-not-hidden-from-http-apis-importable-exportable',
managed: false,
},
],
warnings: [],
});
});
});
});
});
});
}

View file

@ -0,0 +1,137 @@
/*
* 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 path from 'path';
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
const fixturePaths = {
hiddenImportable: path.join(__dirname, 'exports', '_import_hidden_importable.ndjson'),
hiddenNonImportable: path.join(__dirname, 'exports', '_import_hidden_non_importable.ndjson'),
};
export default function ({ getService, getPageObjects }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const svlCommonApi = getService('svlCommonApi');
const testSubjects = getService('testSubjects');
describe('saved objects management with hidden types', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_types'
);
await PageObjects.svlCommonPage.login();
await PageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await PageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_types'
);
await kibanaServer.savedObjects.cleanStandardList();
await PageObjects.svlCommonPage.forceLogout();
});
beforeEach(async () => {
// await PageObjects.svlCommonPage.login();
await PageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await PageObjects.savedObjects.waitTableIsLoaded();
});
describe('API calls', () => {
it('should flag the object as hidden in its meta', async () => {
await supertest
.get('/api/kibana/management/saved_objects/_find?type=test-actions-export-hidden')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((resp) => {
expect(
resp.body.saved_objects.map((obj: any) => ({
id: obj.id,
type: obj.type,
hidden: obj.meta.hiddenType,
}))
).to.eql([
{
id: 'obj_1',
type: 'test-actions-export-hidden',
hidden: true,
},
{
id: 'obj_2',
type: 'test-actions-export-hidden',
hidden: true,
},
]);
});
});
});
describe('Delete modal', () => {
it('should display a warning then trying to delete hidden saved objects', async () => {
await PageObjects.savedObjects.clickCheckboxByTitle('A Pie');
await PageObjects.savedObjects.clickCheckboxByTitle('A Dashboard');
await PageObjects.savedObjects.clickCheckboxByTitle('hidden object 1');
await PageObjects.savedObjects.clickDelete({ confirmDelete: false });
expect(await testSubjects.exists('cannotDeleteObjectsConfirmWarning')).to.eql(true);
});
it('should not delete the hidden objects when performing the operation', async () => {
await PageObjects.savedObjects.clickCheckboxByTitle('A Pie');
await PageObjects.savedObjects.clickCheckboxByTitle('hidden object 1');
await PageObjects.savedObjects.clickDelete({ confirmDelete: true });
const objectNames = (await PageObjects.savedObjects.getTableSummary()).map(
(obj) => obj.title
);
expect(objectNames.includes('hidden object 1')).to.eql(true);
expect(objectNames.includes('A Pie')).to.eql(false);
});
});
describe('importing hidden types', () => {
describe('importable/exportable hidden type', () => {
it('imports objects successfully', async () => {
await PageObjects.savedObjects.importFile(fixturePaths.hiddenImportable);
await PageObjects.savedObjects.checkImportSucceeded();
});
it('shows test-hidden-importable-exportable in table', async () => {
await PageObjects.savedObjects.searchForObject(
'type:(test-hidden-importable-exportable)'
);
const results = await PageObjects.savedObjects.getTableSummary();
expect(results.length).to.be(1);
const { title } = results[0];
expect(title).to.be(
'test-hidden-importable-exportable [id=ff3733a0-9fty-11e7-ahb3-3dcb94193fab]'
);
});
});
describe('non-importable/exportable hidden type', () => {
it('fails to import object', async () => {
await PageObjects.savedObjects.importFile(fixturePaths.hiddenNonImportable);
await PageObjects.savedObjects.checkImportSucceeded();
const errorsCount = await PageObjects.savedObjects.getImportErrorsCount();
expect(errorsCount).to.be(1);
});
});
});
});
}

View file

@ -0,0 +1,85 @@
/*
* 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 path from 'path';
import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const PageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const kibanaServer = getService('kibanaServer');
const testSubjects = getService('testSubjects');
describe('import warnings', () => {
before(async () => {
// emptyKibanaIndex fails in Serverless with
// "index_not_found_exception: no such index [.kibana_ingest]",
// so it was switched to `savedObjects.cleanStandardList()
await kibanaServer.savedObjects.cleanStandardList();
});
beforeEach(async () => {
await PageObjects.svlCommonPage.login();
await PageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await PageObjects.savedObjects.waitTableIsLoaded();
});
it('should display simple warnings', async () => {
await PageObjects.savedObjects.importFile(
path.join(__dirname, 'exports', '_import_type_1.ndjson')
);
await PageObjects.savedObjects.checkImportSucceeded();
const warnings = await PageObjects.savedObjects.getImportWarnings();
expect(warnings).to.eql([
{
message: 'warning for test_import_warning_1',
type: 'simple',
},
]);
});
it('should display action warnings', async () => {
await PageObjects.savedObjects.importFile(
path.join(__dirname, 'exports', '_import_type_2.ndjson')
);
await PageObjects.savedObjects.checkImportSucceeded();
const warnings = await PageObjects.savedObjects.getImportWarnings();
expect(warnings).to.eql([
{
type: 'action_required',
message: 'warning for test_import_warning_2',
},
]);
});
it('should display warnings coming from multiple types', async () => {
await PageObjects.savedObjects.importFile(
path.join(__dirname, 'exports', '_import_both_types.ndjson')
);
await PageObjects.savedObjects.checkImportSucceeded();
const warnings = await PageObjects.savedObjects.getImportWarnings();
expect(warnings).to.eql([
{
message: 'warning for test_import_warning_1',
type: 'simple',
},
{
type: 'action_required',
message: 'warning for test_import_warning_2',
},
]);
});
});
}

View file

@ -0,0 +1,22 @@
/*
* 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 type { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ loadTestFile }: FtrProviderContext) {
describe('Saved Objects Management', function () {
this.tags('skipMKI');
loadTestFile(require.resolve('./find'));
loadTestFile(require.resolve('./scroll_count'));
loadTestFile(require.resolve('./bulk_get'));
loadTestFile(require.resolve('./export_transform'));
loadTestFile(require.resolve('./import_warnings'));
loadTestFile(require.resolve('./hidden_types'));
loadTestFile(require.resolve('./visible_in_management'));
loadTestFile(require.resolve('./hidden_from_http_apis'));
});
}

View file

@ -0,0 +1,63 @@
/*
* 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';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const kibanaServer = getService('kibanaServer');
const apiUrl = '/api/kibana/management/saved_objects/scroll/counts';
const svlCommonApi = getService('svlCommonApi');
const testSubjects = getService('testSubjects');
describe('scroll_count', () => {
describe('saved objects with hidden type', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects'
);
await kibanaServer.importExport.load(
'x-pack/test/functional/fixtures/kbn_archiver/saved_objects_management/hidden_saved_objects'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/hidden_saved_objects'
);
await kibanaServer.savedObjects.clean({
types: ['test-hidden-importable-exportable'],
});
});
it('only counts hidden types that are importableAndExportable', async () => {
const res = await supertest
.post(apiUrl)
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
typesToInclude: [
'test-hidden-non-importable-exportable',
'test-hidden-importable-exportable',
],
})
.expect(200);
expect(res.body).to.eql({
'test-hidden-importable-exportable': 1,
'test-hidden-non-importable-exportable': 0,
});
});
});
});
}

View file

@ -0,0 +1,148 @@
/*
* 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 { join } from 'path';
import expect from '@kbn/expect';
import type { Response } from 'supertest';
import { SavedObject } from '@kbn/core/server';
import type { SavedObjectManagementTypeInfo } from '@kbn/saved-objects-management-plugin/common/types';
import { FtrProviderContext } from '../../../ftr_provider_context';
function parseNdJson(input: string): Array<SavedObject<any>> {
return input.split('\n').map((str) => JSON.parse(str));
}
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const pageObjects = getPageObjects(['common', 'svlCommonPage', 'savedObjects']);
const supertest = getService('supertest');
const esArchiver = getService('esArchiver');
const svlCommonApi = getService('svlCommonApi');
const testSubjects = getService('testSubjects');
describe('types with `visibleInManagement` ', () => {
before(async () => {
await esArchiver.load(
'test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management'
);
await pageObjects.svlCommonPage.login();
await pageObjects.common.navigateToApp('management');
await testSubjects.click('app-card-objects');
await pageObjects.savedObjects.waitTableIsLoaded();
});
after(async () => {
await esArchiver.unload(
'test/functional/fixtures/es_archiver/saved_objects_management/visible_in_management'
);
await pageObjects.svlCommonPage.forceLogout();
});
describe('export', () => {
it('allows to export them directly by id', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
objects: [
{
type: 'test-not-visible-in-management',
id: 'vim-1',
},
],
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.map((obj) => obj.id)).to.eql(['vim-1']);
});
});
it('allows to export them directly by type', async () => {
await supertest
.post('/api/saved_objects/_export')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.send({
type: ['test-not-visible-in-management'],
excludeExportDetails: true,
})
.expect(200)
.then((resp) => {
const objects = parseNdJson(resp.text);
expect(objects.map((obj) => obj.id)).to.eql(['vim-1']);
});
});
});
describe('import', () => {
it('allows to import them', async () => {
await supertest
.post('/api/saved_objects/_import')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.attach('file', join(__dirname, './exports/_import_non_visible_in_management.ndjson'))
.expect(200)
.then((resp) => {
expect(resp.body).to.eql({
success: true,
successCount: 1,
successResults: [
{
id: 'ff3773b0-9ate-11e7-ahb3-3dcb94193fab',
meta: {
title: 'Saved object type that is not visible in management',
},
type: 'test-not-visible-in-management',
managed: false,
},
],
warnings: [],
});
});
});
});
describe('savedObjects management APIS', () => {
describe('GET /api/kibana/management/saved_objects/_allowed_types', () => {
let types: SavedObjectManagementTypeInfo[];
before(async () => {
await supertest
.get('/api/kibana/management/saved_objects/_allowed_types')
.set(svlCommonApi.getCommonRequestHeader())
.set(svlCommonApi.getInternalRequestHeader())
.expect(200)
.then((response: Response) => {
types = response.body.types as SavedObjectManagementTypeInfo[];
});
});
it('should only return types that are `visibleInManagement: true`', () => {
const typeNames = types.map((type) => type.name);
expect(typeNames.includes('test-is-exportable')).to.eql(true);
expect(typeNames.includes('test-visible-in-management')).to.eql(true);
expect(typeNames.includes('test-not-visible-in-management')).to.eql(false);
});
it('should return displayName for types specifying it', () => {
const typeWithDisplayName = types.find((type) => type.name === 'test-with-display-name');
expect(typeWithDisplayName !== undefined).to.eql(true);
expect(typeWithDisplayName!.displayName).to.eql('my display name');
const typeWithoutDisplayName = types.find(
(type) => type.name === 'test-visible-in-management'
);
expect(typeWithoutDisplayName !== undefined).to.eql(true);
expect(typeWithoutDisplayName!.displayName).to.eql('test-visible-in-management');
});
});
});
});
}

View file

@ -0,0 +1,24 @@
/*
* 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 { REPO_ROOT } from '@kbn/repo-info';
import { findTestPluginPaths } from '@kbn/test';
import { resolve } from 'path';
import { createTestConfig } from '../../config.base';
export default createTestConfig({
serverlessProject: 'oblt',
testFiles: [require.resolve('../common/saved_objects_management')],
junit: {
reportName: 'Serverless Search Saved Objects Management Functional Tests',
},
kbnServerArgs: findTestPluginPaths([resolve(REPO_ROOT, 'test/plugin_functional/plugins')]),
// include settings from project controller
// https://github.com/elastic/project-controller/blob/main/internal/project/esproject/config/elasticsearch.yml
esServerArgs: [],
});

View file

@ -0,0 +1,24 @@
/*
* 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 { REPO_ROOT } from '@kbn/repo-info';
import { findTestPluginPaths } from '@kbn/test';
import { resolve } from 'path';
import { createTestConfig } from '../../config.base';
export default createTestConfig({
serverlessProject: 'es',
testFiles: [require.resolve('../common/saved_objects_management')],
junit: {
reportName: 'Serverless Search Saved Objects Management Functional Tests',
},
kbnServerArgs: findTestPluginPaths([resolve(REPO_ROOT, 'test/plugin_functional/plugins')]),
// include settings from project controller
// https://github.com/elastic/project-controller/blob/main/internal/project/esproject/config/elasticsearch.yml
esServerArgs: [],
});

View file

@ -0,0 +1,24 @@
/*
* 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 { REPO_ROOT } from '@kbn/repo-info';
import { findTestPluginPaths } from '@kbn/test';
import { resolve } from 'path';
import { createTestConfig } from '../../config.base';
export default createTestConfig({
serverlessProject: 'security',
testFiles: [require.resolve('../common/saved_objects_management')],
junit: {
reportName: 'Serverless Search Saved Objects Management Functional Tests',
},
kbnServerArgs: findTestPluginPaths([resolve(REPO_ROOT, 'test/plugin_functional/plugins')]),
// include settings from project controller
// https://github.com/elastic/project-controller/blob/main/internal/project/esproject/config/elasticsearch.yml
esServerArgs: [],
});

View file

@ -88,5 +88,6 @@
"@kbn/core",
"@kbn/alerting-plugin",
"@kbn/ftr-common-functional-ui-services",
"@kbn/saved-objects-management-plugin",
]
}