From 14428a97a12d8983bef1290f273981f1284ffe8d Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 20 May 2024 12:33:05 -0700 Subject: [PATCH 01/14] Adds SOM integration tests for serverless --- .../saved_objects_management/bulk_delete.ts | 81 +++ .../saved_objects_management/bulk_get.ts | 82 +++ .../common/saved_objects_management/find.ts | 295 +++++++++++ .../common/saved_objects_management/index.ts | 18 + .../saved_objects_management/relationships.ts | 468 ++++++++++++++++++ .../saved_objects_management/scroll_count.ts | 151 ++++++ .../common_configs/config.group1.ts | 1 + .../search/common_configs/config.group1.ts | 1 + .../security/common_configs/config.group1.ts | 1 + 9 files changed, 1098 insertions(+) create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts new file mode 100644 index 0000000000000..5437b2d27bb72 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts @@ -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 type { Response } from 'supertest'; +import type { FtrProviderContext } from '../../../ftr_provider_context'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + + 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' }; + + 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, { body }: Response) { + const { type, id, error } = body[index]; + expect(type).to.eql(validObject.type); + expect(id).to.eql(validObject.id); + expect(error).to.equal(undefined); + } + + function expectBadRequest(index: number, { body }: Response) { + const { type, id, error } = body[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 supertest + .post(endpoint) + .send([validObject]) + .expect(200) + .then((response: Response) => { + expect(response.body).to.have.length(1); + expectSuccess(0, response); + })); + + it('should return error for invalid object type', async () => + await supertest + .post(endpoint) + .send([invalidObject]) + .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(endpoint) + .send([validObject, invalidObject]) + .expect(200) + .then((response: Response) => { + expect(response.body).to.have.length(2); + expectSuccess(0, response); + expectBadRequest(1, response); + })); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts new file mode 100644 index 0000000000000..7feec9415704b --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts @@ -0,0 +1,82 @@ +/* + * 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 ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + + describe('_bulk_get', () => { + 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' }; + + 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, { body }: Response) { + const { type, id, meta, error } = body[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, { body }: Response) { + const { type, id, error } = body[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 supertest + .post(URL) + .send([validObject]) + .expect(200) + .then((response: Response) => { + expect(response.body).to.have.length(1); + expectSuccess(0, response); + })); + + it('should return error for invalid object type', async () => + await supertest + .post(URL) + .send([invalidObject]) + .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([validObject, invalidObject]) + .expect(200) + .then((response: Response) => { + expect(response.body).to.have.length(2); + expectSuccess(0, response); + expectBadRequest(1, response); + })); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts new file mode 100644 index 0000000000000..0f70e2dab4c73 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts @@ -0,0 +1,295 @@ +/* + * 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 supertest = getService('supertest'); + 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', () => { + 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 supertest + .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 () => + // await supertest + // .get('/api/kibana/management/saved_objects/_find?type=wigwags') + // .expect(200) + // .then((resp: Response) => { + // expect(resp.body).to.eql({ + // page: 1, + // per_page: 20, + // total: 0, + // saved_objects: [], + // }); + // })); + // }); + + // describe('page beyond total', () => { + // it('should return 200 with empty response', async () => + // await supertest + // .get( + // '/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100' + // ) + // .expect(200) + // .then((resp: Response) => { + // expect(resp.body).to.eql({ + // page: 100, + // per_page: 100, + // total: 1, + // saved_objects: [], + // }); + // })); + // }); + + // describe('unknown search field', () => { + // it('should return 400 when using searchFields', async () => + // await supertest + // .get('/api/kibana/management/saved_objects/_find?type=url&searchFields=a') + // .expect(400) + // .then((resp: Response) => { + // expect(resp.body).to.eql({ + // statusCode: 400, + // error: 'Bad Request', + // message: '[request query.searchFields]: definition for this key is missing', + // }); + // })); + // }); + + // describe('`hasReference` and `hasReferenceOperator` parameters', () => { + // before(async () => { + // 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' + // ); + // }); + + // it('search for a reference', async () => { + // await supertest + // .get('/api/kibana/management/saved_objects/_find') + // .query({ + // type: 'visualization', + // hasReference: JSON.stringify({ type: 'ref-type', id: 'ref-1' }), + // }) + // .expect(200) + // .then((resp) => { + // const objects = resp.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 supertest + // .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', + // }) + // .expect(200) + // .then((resp) => { + // const objects = resp.body.saved_objects; + // expect(objects.map((obj: any) => obj.id)).to.eql([ + // 'only-ref-1', + // 'only-ref-2', + // 'ref-1-and-ref-2', + // ]); + // }); + // }); + + // it('search for multiple references with AND operator', async () => { + // await supertest + // .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', + // }) + // .expect(200) + // .then((resp) => { + // const objects = resp.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 () => { + // await supertest + // .get('/api/kibana/management/saved_objects/_find') + // .query({ + // type: ['visualization', 'dashboard'], + // sortField: 'type', + // sortOrder: 'asc', + // }) + // .expect(200) + // .then((resp) => { + // const objects = resp.body.saved_objects; + // expect(objects.length).be.greaterThan(1); // Need more than 1 result for our test + // expect(objects[0].type).to.be('dashboard'); + // }); + // }); + + // it('sort objects by "type" in "desc" order', async () => { + // await supertest + // .get('/api/kibana/management/saved_objects/_find') + // .query({ + // type: ['visualization', 'dashboard'], + // sortField: 'type', + // sortOrder: 'desc', + // }) + // .expect(200) + // .then((resp) => { + // const objects = resp.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 supertest + // .get('/api/kibana/management/saved_objects/_find?type=search') + // .expect(200) + // .then((resp: Response) => { + // expect(resp.body.saved_objects).to.have.length(1); + // expect(resp.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 supertest + // .get('/api/kibana/management/saved_objects/_find?type=dashboard') + // .expect(200) + // .then((resp: Response) => { + // expect(resp.body.saved_objects).to.have.length(1); + // expect(resp.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 supertest + // .get('/api/kibana/management/saved_objects/_find?type=visualization') + // .expect(200) + // .then((resp: Response) => { + // expect(resp.body.saved_objects).to.have.length(2); + // expect(resp.body.saved_objects[0].meta).to.eql({ + // icon: 'visualizeApp', + // title: 'VisualizationFromSavedSearch', + // hiddenType: false, + // inAppUrl: { + // path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + // uiCapabilitiesPath: 'visualize.show', + // }, + // namespaceType: 'multiple-isolated', + // }); + // expect(resp.body.saved_objects[1].meta).to.eql({ + // icon: 'visualizeApp', + // title: 'Visualization', + // hiddenType: false, + // inAppUrl: { + // path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + // uiCapabilitiesPath: 'visualize.show', + // }, + // namespaceType: 'multiple-isolated', + // }); + // })); + + // it('should inject meta attributes for index patterns', async () => + // await supertest + // .get('/api/kibana/management/saved_objects/_find?type=index-pattern') + // .expect(200) + // .then((resp: Response) => { + // expect(resp.body.saved_objects).to.have.length(1); + // expect(resp.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', + // }); + // })); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts new file mode 100644 index 0000000000000..3ff13672aaaaa --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts @@ -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_delete')); + // loadTestFile(require.resolve('./bulk_get')); + // loadTestFile(require.resolve('./relationships')); + // loadTestFile(require.resolve('./scroll_count')); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts new file mode 100644 index 0000000000000..532b6dc418ddc --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts @@ -0,0 +1,468 @@ +/* + * 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'; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + + 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()), + inAppUrl: 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 () => { + 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' + ); + }); + + 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('searches', () => { + it('should validate search response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for searches', async () => { + const resp = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .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', + inAppUrl: { + path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get( + relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) + ) + .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', + inAppUrl: { + path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + relationship: 'parent', + }, + ]); + }); + + it('should return 404 if search finds no results', async () => { + await supertest + .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + + describe('dashboards', () => { + it('should validate dashboard response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for dashboards', async () => { + const resp = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(resp.body.relations).to.eql([ + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'child', + meta: { + icon: 'visualizeApp', + title: 'Visualization', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'child', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + inAppUrl: { + path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) + .expect(200); + + expect(resp.body.relations).to.eql([ + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'Visualization', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + relationship: 'child', + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + inAppUrl: { + path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + relationship: 'child', + }, + ]); + }); + + it('should return 404 if dashboard finds no results', async () => { + await supertest + .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + + describe('visualizations', () => { + it('should validate visualization response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for visualizations', async () => { + const resp = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .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('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get( + relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) + ) + .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('should return 404 if visualizations finds no results', async () => { + await supertest + .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + + describe('index patterns', () => { + it('should validate visualization response schema', async () => { + const resp = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should work for index patterns', async () => { + const resp = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .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', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const resp = await supertest + .get( + relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) + ) + .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', + }, + ]); + }); + + it('should return 404 if index pattern finds no results', async () => { + await supertest + .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .expect(404); + }); + }); + + describe('invalid references', () => { + it('should validate the response schema', async () => { + const resp = await supertest.get(relationshipsUrl('dashboard', 'invalid-refs')).expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); + }); + + it('should return the invalid relations', async () => { + const resp = await supertest.get(relationshipsUrl('dashboard', 'invalid-refs')).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', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + title: 'Visualization', + }, + relationship: 'child', + type: 'visualization', + }, + ], + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts new file mode 100644 index 0000000000000..cf7272243add9 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts @@ -0,0 +1,151 @@ +/* + * 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'; + +const apiUrl = '/api/kibana/management/saved_objects/scroll/counts'; +const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; + +export default function ({ getService }: FtrProviderContext) { + const supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + const esArchiver = getService('esArchiver'); + + describe('scroll_count', () => { + 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 res = await supertest + .post(apiUrl) + .send({ + typesToInclude: defaultTypes, + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 2, + 'index-pattern': 1, + search: 1, + visualization: 2, + }); + }); + + it('only returns count for types to include', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['dashboard', 'search'], + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 2, + search: 1, + }); + }); + + it('filters on title when `searchString` is provided', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: defaultTypes, + searchString: 'Amazing', + }) + .expect(200); + + expect(res.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 res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['dashboard', 'search', 'visualization'], + searchString: 'nothing-will-match', + }) + .expect(200); + + expect(res.body).to.eql({ + dashboard: 0, + visualization: 0, + search: 0, + }); + }); + }); + + 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 supertest + .post(`/api/saved_objects/_import`) + .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') + .expect(200); + }; + + before(async () => { + await importVisualizations({ startIdx: 1, endIdx: 6000 }); + await importVisualizations({ startIdx: 6001, endIdx: 12000 }); + }); + after(async () => { + await esArchiver.emptyKibanaIndex(); + }); + + it('returns the correct count for each included types', async () => { + const res = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['visualization'], + }) + .expect(200); + + expect(res.body).to.eql({ + visualization: 12000, + }); + }); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts b/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts index 58635777c0181..89f72c1d46f19 100644 --- a/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts +++ b/x-pack/test_serverless/api_integration/test_suites/observability/common_configs/config.group1.ts @@ -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'), ], junit: { reportName: 'Serverless Observability API Integration Tests - Common Group 1', diff --git a/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts b/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts index 76d1824fd1451..fd28f48e7db5f 100644 --- a/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts +++ b/x-pack/test_serverless/api_integration/test_suites/search/common_configs/config.group1.ts @@ -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'), ], junit: { reportName: 'Serverless Search API Integration Tests - Common Group 1', diff --git a/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts b/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts index f9d283daf13ed..37d0cbcba8557 100644 --- a/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts +++ b/x-pack/test_serverless/api_integration/test_suites/security/common_configs/config.group1.ts @@ -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'), ], junit: { reportName: 'Serverless Security API Integration Tests - Common Group 1', From ba714a365e6cb3e46146dfdba4dbf6c01110e649 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 21 May 2024 14:19:34 -0700 Subject: [PATCH 02/14] Adds assertion for find --- .../src/lib/apis/find.ts | 1 - .../saved_objects_management/bulk_delete.ts | 81 --- .../saved_objects_management/bulk_get.ts | 82 --- .../common/saved_objects_management/find.ts | 470 +++++++++--------- .../common/saved_objects_management/index.ts | 4 - .../saved_objects_management/relationships.ts | 468 ----------------- .../saved_objects_management/scroll_count.ts | 151 ------ 7 files changed, 239 insertions(+), 1018 deletions(-) delete mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts delete mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts delete mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts delete mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts diff --git a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts index 62b5ccb82d68d..c7c66c4cbfe30 100644 --- a/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts +++ b/packages/core/saved-objects/core-saved-objects-api-server-internal/src/lib/apis/find.ts @@ -91,7 +91,6 @@ export const performFind = async ( aggs, migrationVersionCompatibility, } = options; - if (!type) { throw SavedObjectsErrorHelpers.createBadRequestError( 'options.type must be a string or an array of strings' diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts deleted file mode 100644 index 5437b2d27bb72..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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 ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const kibanaServer = getService('kibanaServer'); - - 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' }; - - 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, { body }: Response) { - const { type, id, error } = body[index]; - expect(type).to.eql(validObject.type); - expect(id).to.eql(validObject.id); - expect(error).to.equal(undefined); - } - - function expectBadRequest(index: number, { body }: Response) { - const { type, id, error } = body[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 supertest - .post(endpoint) - .send([validObject]) - .expect(200) - .then((response: Response) => { - expect(response.body).to.have.length(1); - expectSuccess(0, response); - })); - - it('should return error for invalid object type', async () => - await supertest - .post(endpoint) - .send([invalidObject]) - .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(endpoint) - .send([validObject, invalidObject]) - .expect(200) - .then((response: Response) => { - expect(response.body).to.have.length(2); - expectSuccess(0, response); - expectBadRequest(1, response); - })); - }); -} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts deleted file mode 100644 index 7feec9415704b..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts +++ /dev/null @@ -1,82 +0,0 @@ -/* - * 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 ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const kibanaServer = getService('kibanaServer'); - - describe('_bulk_get', () => { - 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' }; - - 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, { body }: Response) { - const { type, id, meta, error } = body[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, { body }: Response) { - const { type, id, error } = body[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 supertest - .post(URL) - .send([validObject]) - .expect(200) - .then((response: Response) => { - expect(response.body).to.have.length(1); - expectSuccess(0, response); - })); - - it('should return error for invalid object type', async () => - await supertest - .post(URL) - .send([invalidObject]) - .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([validObject, invalidObject]) - .expect(200) - .then((response: Response) => { - expect(response.body).to.have.length(2); - expectSuccess(0, response); - expectBadRequest(1, response); - })); - }); -} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts index 0f70e2dab4c73..c4bd5ef95fb41 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts @@ -4,7 +4,7 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ - +// See x-pack/test_serverless/api_integration/test_suites/common/core/translations.ts import expect from '@kbn/expect'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { RoleCredentials } from '../../../../shared/services'; @@ -47,249 +47,257 @@ export default function ({ getService }: FtrProviderContext) { ]); }); - // describe('unknown type', () => { - // it('should return 200 with empty response', async () => - // await supertest - // .get('/api/kibana/management/saved_objects/_find?type=wigwags') - // .expect(200) - // .then((resp: Response) => { - // expect(resp.body).to.eql({ - // page: 1, - // per_page: 20, - // total: 0, - // saved_objects: [], - // }); - // })); - // }); + describe('unknown type', () => { + it('should return 200 with empty response', async () => { + const { body } = await supertest + .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 () => - // await supertest - // .get( - // '/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100' - // ) - // .expect(200) - // .then((resp: Response) => { - // expect(resp.body).to.eql({ - // page: 100, - // per_page: 100, - // total: 1, - // saved_objects: [], - // }); - // })); - // }); + describe('page beyond total', () => { + it('should return 200 with empty response', async () => { + const { body } = await supertest + .get( + '/api/kibana/management/saved_objects/_find?type=visualization&page=100&perPage=100' + ) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); - // describe('unknown search field', () => { - // it('should return 400 when using searchFields', async () => - // await supertest - // .get('/api/kibana/management/saved_objects/_find?type=url&searchFields=a') - // .expect(400) - // .then((resp: Response) => { - // expect(resp.body).to.eql({ - // statusCode: 400, - // error: 'Bad Request', - // message: '[request query.searchFields]: definition for this key is missing', - // }); - // })); - // }); + expect(body).to.eql({ + page: 100, + per_page: 100, + total: 1, + saved_objects: [], + }); + }); + }); - // describe('`hasReference` and `hasReferenceOperator` parameters', () => { - // before(async () => { - // 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' - // ); - // }); + describe('unknown search field', () => { + it('should return 400 when using searchFields', async () => { + const { body } = await supertest + .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', + }); + }); + }); - // it('search for a reference', async () => { - // await supertest - // .get('/api/kibana/management/saved_objects/_find') - // .query({ - // type: 'visualization', - // hasReference: JSON.stringify({ type: 'ref-type', id: 'ref-1' }), - // }) - // .expect(200) - // .then((resp) => { - // const objects = resp.body.saved_objects; - // expect(objects.map((obj: any) => obj.id)).to.eql(['only-ref-1', 'ref-1-and-ref-2']); - // }); - // }); + describe('`hasReference` and `hasReferenceOperator` parameters', () => { + before(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + 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' + ); + }); - // it('search for multiple references with OR operator', async () => { - // await supertest - // .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', - // }) - // .expect(200) - // .then((resp) => { - // const objects = resp.body.saved_objects; - // expect(objects.map((obj: any) => obj.id)).to.eql([ - // 'only-ref-1', - // 'only-ref-2', - // 'ref-1-and-ref-2', - // ]); - // }); - // }); + it('search for a reference', async () => { + const { body } = await supertest + .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']); + }); + }); + // does not work in serverless mode + it.skip('search for multiple references with OR operator', async () => { + await supertest + .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 + ); + }); + }); + // does not work in serverless mode + it.skip('search for multiple references with AND operator', async () => { + const { body } = await supertest + .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']); + }); - // it('search for multiple references with AND operator', async () => { - // await supertest - // .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', - // }) - // .expect(200) - // .then((resp) => { - // const objects = resp.body.saved_objects; - // expect(objects.map((obj: any) => obj.id)).to.eql(['ref-1-and-ref-2']); - // }); - // }); - // }); + describe.skip('`sortField` and `sortOrder` parameters', () => { + // does not work in serverless mode + it('sort objects by "type" in "asc" order', async () => { + const { body } = await supertest + .get('/api/kibana/management/saved_objects/_find') + .query({ + type: ['visualization', 'dashboard'], + sortField: 'type', + sortOrder: 'asc', + }) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); - // describe('`sortField` and `sortOrder` parameters', () => { - // it('sort objects by "type" in "asc" order', async () => { - // await supertest - // .get('/api/kibana/management/saved_objects/_find') - // .query({ - // type: ['visualization', 'dashboard'], - // sortField: 'type', - // sortOrder: 'asc', - // }) - // .expect(200) - // .then((resp) => { - // const objects = resp.body.saved_objects; - // expect(objects.length).be.greaterThan(1); // Need more than 1 result for our test - // expect(objects[0].type).to.be('dashboard'); - // }); - // }); + 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 supertest + .get('/api/kibana/management/saved_objects/_find') + .query({ + type: ['visualization', 'dashboard'], + sortField: 'type', + sortOrder: 'desc', + }) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); - // it('sort objects by "type" in "desc" order', async () => { - // await supertest - // .get('/api/kibana/management/saved_objects/_find') - // .query({ - // type: ['visualization', 'dashboard'], - // sortField: 'type', - // sortOrder: 'desc', - // }) - // .expect(200) - // .then((resp) => { - // const objects = resp.body.saved_objects; - // expect(objects[0].type).to.be('visualization'); - // }); - // }); - // }); - // }); + 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(); - // }); + 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 supertest - // .get('/api/kibana/management/saved_objects/_find?type=search') - // .expect(200) - // .then((resp: Response) => { - // expect(resp.body.saved_objects).to.have.length(1); - // expect(resp.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 searches', async () => + await supertest + .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 supertest - // .get('/api/kibana/management/saved_objects/_find?type=dashboard') - // .expect(200) - // .then((resp: Response) => { - // expect(resp.body.saved_objects).to.have.length(1); - // expect(resp.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 dashboards', async () => + await supertest + .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 supertest - // .get('/api/kibana/management/saved_objects/_find?type=visualization') - // .expect(200) - // .then((resp: Response) => { - // expect(resp.body.saved_objects).to.have.length(2); - // expect(resp.body.saved_objects[0].meta).to.eql({ - // icon: 'visualizeApp', - // title: 'VisualizationFromSavedSearch', - // hiddenType: false, - // inAppUrl: { - // path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', - // uiCapabilitiesPath: 'visualize.show', - // }, - // namespaceType: 'multiple-isolated', - // }); - // expect(resp.body.saved_objects[1].meta).to.eql({ - // icon: 'visualizeApp', - // title: 'Visualization', - // hiddenType: false, - // inAppUrl: { - // path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - // uiCapabilitiesPath: 'visualize.show', - // }, - // namespaceType: 'multiple-isolated', - // }); - // })); + it('should inject meta attributes for visualizations', async () => + await supertest + .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 supertest - // .get('/api/kibana/management/saved_objects/_find?type=index-pattern') - // .expect(200) - // .then((resp: Response) => { - // expect(resp.body.saved_objects).to.have.length(1); - // expect(resp.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', - // }); - // })); + it('should inject meta attributes for index patterns', async () => + await supertest + .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', + }); + })); }); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts index 3ff13672aaaaa..5a1d9cc281cce 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts @@ -10,9 +10,5 @@ import { FtrProviderContext } from '../../../ftr_provider_context'; export default function ({ loadTestFile }: FtrProviderContext) { describe('saved objects management apis', () => { loadTestFile(require.resolve('./find')); - // loadTestFile(require.resolve('./bulk_delete')); - // loadTestFile(require.resolve('./bulk_get')); - // loadTestFile(require.resolve('./relationships')); - // loadTestFile(require.resolve('./scroll_count')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts deleted file mode 100644 index 532b6dc418ddc..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts +++ /dev/null @@ -1,468 +0,0 @@ -/* - * 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'; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const kibanaServer = getService('kibanaServer'); - - 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()), - inAppUrl: 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 () => { - 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' - ); - }); - - 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('searches', () => { - it('should validate search response schema', async () => { - const resp = await supertest - .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) - .expect(200); - - expect(() => { - responseSchema.validate(resp.body); - }).not.to.throwError(); - }); - - it('should work for searches', async () => { - const resp = await supertest - .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) - .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', - inAppUrl: { - path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - }, - ]); - }); - - it('should filter based on savedObjectTypes', async () => { - const resp = await supertest - .get( - relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) - ) - .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', - inAppUrl: { - path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - relationship: 'parent', - }, - ]); - }); - - it('should return 404 if search finds no results', async () => { - await supertest - .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .expect(404); - }); - }); - - describe('dashboards', () => { - it('should validate dashboard response schema', async () => { - const resp = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) - .expect(200); - - expect(() => { - responseSchema.validate(resp.body); - }).not.to.throwError(); - }); - - it('should work for dashboards', async () => { - const resp = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) - .expect(200); - - expect(resp.body.relations).to.eql([ - { - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'child', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - }, - { - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'child', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - inAppUrl: { - path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - }, - ]); - }); - - it('should filter based on savedObjectTypes', async () => { - const resp = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) - .expect(200); - - expect(resp.body.relations).to.eql([ - { - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - relationship: 'child', - }, - { - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - inAppUrl: { - path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - relationship: 'child', - }, - ]); - }); - - it('should return 404 if dashboard finds no results', async () => { - await supertest - .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .expect(404); - }); - }); - - describe('visualizations', () => { - it('should validate visualization response schema', async () => { - const resp = await supertest - .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) - .expect(200); - - expect(() => { - responseSchema.validate(resp.body); - }).not.to.throwError(); - }); - - it('should work for visualizations', async () => { - const resp = await supertest - .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) - .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('should filter based on savedObjectTypes', async () => { - const resp = await supertest - .get( - relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) - ) - .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('should return 404 if visualizations finds no results', async () => { - await supertest - .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .expect(404); - }); - }); - - describe('index patterns', () => { - it('should validate visualization response schema', async () => { - const resp = await supertest - .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) - .expect(200); - - expect(() => { - responseSchema.validate(resp.body); - }).not.to.throwError(); - }); - - it('should work for index patterns', async () => { - const resp = await supertest - .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) - .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', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - }, - ]); - }); - - it('should filter based on savedObjectTypes', async () => { - const resp = await supertest - .get( - relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) - ) - .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', - }, - ]); - }); - - it('should return 404 if index pattern finds no results', async () => { - await supertest - .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .expect(404); - }); - }); - - describe('invalid references', () => { - it('should validate the response schema', async () => { - const resp = await supertest.get(relationshipsUrl('dashboard', 'invalid-refs')).expect(200); - - expect(() => { - responseSchema.validate(resp.body); - }).not.to.throwError(); - }); - - it('should return the invalid relations', async () => { - const resp = await supertest.get(relationshipsUrl('dashboard', 'invalid-refs')).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', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, - namespaceType: 'multiple-isolated', - hiddenType: false, - title: 'Visualization', - }, - relationship: 'child', - type: 'visualization', - }, - ], - }); - }); - }); - }); -} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts deleted file mode 100644 index cf7272243add9..0000000000000 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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'; - -const apiUrl = '/api/kibana/management/saved_objects/scroll/counts'; -const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; - -export default function ({ getService }: FtrProviderContext) { - const supertest = getService('supertest'); - const kibanaServer = getService('kibanaServer'); - const esArchiver = getService('esArchiver'); - - describe('scroll_count', () => { - 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 res = await supertest - .post(apiUrl) - .send({ - typesToInclude: defaultTypes, - }) - .expect(200); - - expect(res.body).to.eql({ - dashboard: 2, - 'index-pattern': 1, - search: 1, - visualization: 2, - }); - }); - - it('only returns count for types to include', async () => { - const res = await supertest - .post(apiUrl) - .send({ - typesToInclude: ['dashboard', 'search'], - }) - .expect(200); - - expect(res.body).to.eql({ - dashboard: 2, - search: 1, - }); - }); - - it('filters on title when `searchString` is provided', async () => { - const res = await supertest - .post(apiUrl) - .send({ - typesToInclude: defaultTypes, - searchString: 'Amazing', - }) - .expect(200); - - expect(res.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 res = await supertest - .post(apiUrl) - .send({ - typesToInclude: ['dashboard', 'search', 'visualization'], - searchString: 'nothing-will-match', - }) - .expect(200); - - expect(res.body).to.eql({ - dashboard: 0, - visualization: 0, - search: 0, - }); - }); - }); - - 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 supertest - .post(`/api/saved_objects/_import`) - .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') - .expect(200); - }; - - before(async () => { - await importVisualizations({ startIdx: 1, endIdx: 6000 }); - await importVisualizations({ startIdx: 6001, endIdx: 12000 }); - }); - after(async () => { - await esArchiver.emptyKibanaIndex(); - }); - - it('returns the correct count for each included types', async () => { - const res = await supertest - .post(apiUrl) - .send({ - typesToInclude: ['visualization'], - }) - .expect(200); - - expect(res.body).to.eql({ - visualization: 12000, - }); - }); - }); - }); -} From 248289291d6b88089d69a4bfd376cdca8bec05b2 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Fri, 24 May 2024 18:10:26 -0700 Subject: [PATCH 03/14] adds tests for bulk_get --- .../saved_objects_management/bulk_get.ts | 102 ++++++++++++++++++ .../common/saved_objects_management/index.ts | 1 + 2 files changed, 103 insertions(+) create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts new file mode 100644 index 0000000000000..03cb8d39b7dfb --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_get.ts @@ -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 supertest = getService('supertest'); + 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 supertest + .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 supertest + .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 supertest + .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); + })); + }); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts index 5a1d9cc281cce..fd066c6f11566 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts @@ -10,5 +10,6 @@ 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')); }); } From 0eff45c78e4875c329e6d8ac8512e64e6cb7aec1 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 3 Jun 2024 10:24:38 -0700 Subject: [PATCH 04/14] adds tests for _bulk_delete --- .../saved_objects_management/bulk_delete.ts | 99 +++++++++++++++++++ .../common/saved_objects_management/index.ts | 1 + 2 files changed, 100 insertions(+) create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts new file mode 100644 index 0000000000000..911a1c7ad2a9a --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/bulk_delete.ts @@ -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 supertest = getService('supertest'); + 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 supertest + .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 supertest + .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 supertest + .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); + })); + }); +} diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts index fd066c6f11566..391bbd23cb0ae 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts @@ -11,5 +11,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { describe('saved objects management apis', () => { loadTestFile(require.resolve('./find')); loadTestFile(require.resolve('./bulk_get')); + loadTestFile(require.resolve('./bulk_delete')); }); } From 601e55f9d2507a78d6cad35458c79fb7b061190b Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 3 Jun 2024 11:58:58 -0700 Subject: [PATCH 05/14] Adds scroll_count --- .../common/saved_objects_management/index.ts | 1 + .../saved_objects_management/scroll_count.ts | 173 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts index 391bbd23cb0ae..f388f61e309cd 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts @@ -12,5 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./find')); loadTestFile(require.resolve('./bulk_get')); loadTestFile(require.resolve('./bulk_delete')); + loadTestFile(require.resolve('./scroll_count')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts new file mode 100644 index 0000000000000..482791dd47e53 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts @@ -0,0 +1,173 @@ +/* + * 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'; + +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 supertest = getService('supertest'); + const kibanaServer = getService('kibanaServer'); + let roleAuthc: RoleCredentials; + + describe('scroll_count', () => { + before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + }); + + after(async () => { + await svlUserManager.invalidateApiKeyForRole(roleAuthc); + }); + + 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 supertest + .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 supertest + .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 supertest + .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 supertest + .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, + }); + }); + }); + + 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 supertest + .post(`/api/saved_objects/_import`) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') + .expect(200); + }; + + before(async () => { + await importVisualizations({ startIdx: 1, endIdx: 6000 }); + await importVisualizations({ startIdx: 6001, endIdx: 12000 }); + }); + after(async () => { + await kibanaServer.savedObjects.cleanStandardList(); + }); + + it('returns the correct count for each included types', async () => { + const { body } = await supertest + .post(apiUrl) + .send({ + typesToInclude: ['visualization'], + }) + .expect(200); + + expect(body).to.eql({ + visualization: 12000, + }); + }); + }); + }); +} From 907dc33327f3458118f8ef980ff47d1445dc6c7f Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Mon, 3 Jun 2024 17:23:06 -0700 Subject: [PATCH 06/14] Adds scroll_count --- .../saved_objects_management/scroll_count.ts | 131 ++++++++++-------- 1 file changed, 76 insertions(+), 55 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts index 482791dd47e53..089515844b30d 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/scroll_count.ts @@ -28,7 +28,83 @@ export default function ({ getService }: FtrProviderContext) { 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 supertest + .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 supertest + .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(); @@ -114,60 +190,5 @@ export default function ({ getService }: FtrProviderContext) { }); }); }); - - 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 supertest - .post(`/api/saved_objects/_import`) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .attach('file', Buffer.from(fileChunks.join('\n'), 'utf8'), 'export.ndjson') - .expect(200); - }; - - before(async () => { - await importVisualizations({ startIdx: 1, endIdx: 6000 }); - await importVisualizations({ startIdx: 6001, endIdx: 12000 }); - }); - after(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - }); - - it('returns the correct count for each included types', async () => { - const { body } = await supertest - .post(apiUrl) - .send({ - typesToInclude: ['visualization'], - }) - .expect(200); - - expect(body).to.eql({ - visualization: 12000, - }); - }); - }); }); } From 80329177053f9988ef0c922ccd47e777e7673fdb Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Tue, 4 Jun 2024 15:19:27 -0700 Subject: [PATCH 07/14] Add relationships api integration tests --- .../common/saved_objects_management/index.ts | 1 + .../saved_objects_management/relationships.ts | 531 ++++++++++++++++++ 2 files changed, 532 insertions(+) create mode 100644 x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts index f388f61e309cd..2da0263122550 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/index.ts @@ -13,5 +13,6 @@ export default function ({ loadTestFile }: FtrProviderContext) { loadTestFile(require.resolve('./bulk_get')); loadTestFile(require.resolve('./bulk_delete')); loadTestFile(require.resolve('./scroll_count')); + loadTestFile(require.resolve('./relationships')); }); } diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts new file mode 100644 index 0000000000000..4dbcafbb734c7 --- /dev/null +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts @@ -0,0 +1,531 @@ +/* + * 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 { SavedObjectRelation } from '@kbn/saved-objects-management-plugin/public'; +import { FtrProviderContext } from '../../../ftr_provider_context'; +import { RoleCredentials } from '../../../../shared/services'; + +// TODO: convert to supertestWithoutAuth + +export default function ({ getService }: FtrProviderContext) { + const svlCommonApi = getService('svlCommonApi'); + const svlUserManager = getService('svlUserManager'); + const supertest = getService('supertest'); + 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()), + // dashboard fixture doesn't appear to have an inAppUrl + inAppUrl: schema.object({ + path: schema.maybe(schema.string()), + uiCapabilitiesPath: schema.maybe(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', () => { + 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}`; + }; + let relations1: SavedObjectRelation; + let relations2: SavedObjectRelation; + + before(async () => { + roleAuthc = await svlUserManager.createApiKeyForRole('admin'); + }); + + after(async () => { + await svlUserManager.invalidateApiKeyForRole(roleAuthc); + }); + + describe('searches', () => { + before(async () => { + 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(); + }); + + it('should validate search response schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + expect(() => { + relationSchema.validate(relations1); + }).not.to.throwException(); + }); + + it('should work for searches', async () => { + const { body } = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).to.eql({ + id: '8963ca30-3224-11e8-a572-ffca06da1357', + meta: { + editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', + hiddenType: false, + icon: 'indexPatternApp', + inAppUrl: { + path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'management.kibana.indexPatterns', + }, + namespaceType: 'multiple', + title: 'saved_objects*', + }, + relationship: 'child', + type: 'index-pattern', + }); + expect(relations2).to.eql({ + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + relationship: 'parent', + }); + }); + + it('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get( + relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) + ) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).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', + }); + expect(relations2).to.eql({ + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + meta: { + hiddenType: false, + icon: 'visualizeApp', + namespaceType: 'multiple-isolated', + title: 'VisualizationFromSavedSearch', + }, + relationship: 'parent', + type: 'visualization', + }); + }); + + it('should return 404 if search finds no results', async () => { + await supertest + .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); + }); + + describe('dashboards', () => { + before(async () => { + 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(); + }); + + it('should validate dashboard response schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + + expect(() => { + relationSchema.validate(relations1); + }).not.to.throwException(); + }); + + it.skip('should work for dashboards', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(body.relations).to.eql([ + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'child', + meta: { + icon: 'visualizeApp', + title: 'Visualization', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + relationship: 'child', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + inAppUrl: { + path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + }, + ]); + }); + + it.skip('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(body.relations).to.eql([ + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'Visualization', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + relationship: 'child', + }, + { + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + inAppUrl: { + path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + relationship: 'child', + }, + ]); + }); + + it.skip('should return 404 if dashboard finds no results', async () => { + await supertest + .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); + }); + + describe.skip('visualizations', () => { + it('should validate visualization response schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + responseSchema.validate(body); + }).not.to.throwError(); + }); + + it('should work for visualizations', async () => { + const { body } = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(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('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get( + relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) + ) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(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('should return 404 if visualizations finds no results', async () => { + await supertest + .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); + }); + + describe.skip('index patterns', () => { + it('should validate visualization response schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + responseSchema.validate(body); + }).not.to.throwError(); + }); + + it('should work for index patterns', async () => { + const { body } = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(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', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + }, + ]); + }); + + it('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get( + relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) + ) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(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', + }, + ]); + }); + + it('should return 404 if index pattern finds no results', async () => { + await supertest + .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); + }); + + describe.skip('invalid references', () => { + it('should validate the response schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'invalid-refs')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + responseSchema.validate(body); + }).not.to.throwError(); + }); + + it('should return the invalid relations', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'invalid-refs')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(body.invalidRelations).to.eql([ + { + error: 'Saved object [visualization/invalid-vis] not found', + id: 'invalid-vis', + relationship: 'child', + type: 'visualization', + }, + ]); + expect(body.relations).to.eql([ + { + id: 'add810b0-3224-11e8-a572-ffca06da1357', + meta: { + icon: 'visualizeApp', + inAppUrl: { + path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'visualize.show', + }, + namespaceType: 'multiple-isolated', + hiddenType: false, + title: 'Visualization', + }, + relationship: 'child', + type: 'visualization', + }, + ]); + }); + }); + }); +} From 2446ef2d16cb7b81c4096edbb10c37379ad80ab9 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 6 Jun 2024 09:30:27 +0000 Subject: [PATCH 08/14] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/test_serverless/tsconfig.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index 5dff8ab3431fd..cb62b970782a8 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -101,6 +101,7 @@ "@kbn/dataset-quality-plugin", "@kbn/alerting-comparators", "@kbn/search-types", - "@kbn/reporting-server" + "@kbn/reporting-server", + "@kbn/config-schema" ] } From 43afb958fa300ccaad5941107f6f42038fe2b6c0 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Thu, 6 Jun 2024 10:51:51 -0700 Subject: [PATCH 09/14] Fix syntax --- x-pack/test_serverless/tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/test_serverless/tsconfig.json b/x-pack/test_serverless/tsconfig.json index 390fce4177b27..95b99a8a08f20 100644 --- a/x-pack/test_serverless/tsconfig.json +++ b/x-pack/test_serverless/tsconfig.json @@ -102,7 +102,7 @@ "@kbn/alerting-comparators", "@kbn/search-types", "@kbn/reporting-server", - "@kbn/config-schema" - "@kbn/features-plugin" + "@kbn/config-schema", + "@kbn/features-plugin", ] } From b34e97c11a7017b3e549b40e38fcaab26b8c43a0 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Thu, 6 Jun 2024 12:55:51 -0700 Subject: [PATCH 10/14] remove unused --- .../test_suites/common/saved_objects_management/relationships.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts index 4dbcafbb734c7..ad59cf180c19f 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts @@ -17,7 +17,6 @@ export default function ({ getService }: FtrProviderContext) { const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); const supertest = getService('supertest'); - const supertestWithoutAuth = getService('supertestWithoutAuth'); const kibanaServer = getService('kibanaServer'); let roleAuthc: RoleCredentials; From 8434dfd8b90794ec1a04dc3372dccd95d8e56d07 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Thu, 6 Jun 2024 16:59:02 -0700 Subject: [PATCH 11/14] Refactors relationships tests --- .../saved_objects_management/relationships.ts | 565 ++++++++---------- 1 file changed, 260 insertions(+), 305 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts index ad59cf180c19f..194fa0997baf2 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts @@ -28,11 +28,13 @@ export default function ({ getService }: FtrProviderContext) { title: schema.string(), icon: schema.string(), editUrl: schema.maybe(schema.string()), - // dashboard fixture doesn't appear to have an inAppUrl - inAppUrl: schema.object({ - path: schema.maybe(schema.string()), - uiCapabilitiesPath: schema.maybe(schema.string()), - }), + // dashboards and visualizations don't declare an inAppUrl + inAppUrl: schema.maybe( + schema.object({ + path: schema.string(), + uiCapabilitiesPath: schema.string(), + }) + ), namespaceType: schema.string(), hiddenType: schema.boolean(), }), @@ -44,11 +46,6 @@ export default function ({ getService }: FtrProviderContext) { error: schema.string(), }); - const responseSchema = schema.object({ - relations: schema.arrayOf(relationSchema), - invalidRelations: schema.arrayOf(invalidRelationSchema), - }); - describe('relationships', () => { const baseApiUrl = `/api/kibana/management/saved_objects/relationships`; const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; @@ -68,7 +65,7 @@ export default function ({ getService }: FtrProviderContext) { await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); - describe('searches', () => { + describe('defaultTypes relations', () => { before(async () => { await kibanaServer.savedObjects.cleanStandardList(); await kibanaServer.importExport.load( @@ -81,244 +78,213 @@ export default function ({ getService }: FtrProviderContext) { ); await kibanaServer.savedObjects.cleanStandardList(); }); + describe('searches', () => { + it('should validate search relationships schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + expect(() => { + relationSchema.validate(body.relations[0]); + }).not.to.throwError(); + }); - it('should validate search response schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - expect(() => { - relationSchema.validate(relations1); - }).not.to.throwException(); - }); - - it('should work for searches', async () => { - const { body } = await supertest - .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).to.eql({ - id: '8963ca30-3224-11e8-a572-ffca06da1357', - meta: { - editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', - hiddenType: false, - icon: 'indexPatternApp', - inAppUrl: { - path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'management.kibana.indexPatterns', + it('should work for searches', async () => { + const { body } = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).to.eql({ + id: '8963ca30-3224-11e8-a572-ffca06da1357', + meta: { + editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', + hiddenType: false, + icon: 'indexPatternApp', + inAppUrl: { + path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', + uiCapabilitiesPath: 'management.kibana.indexPatterns', + }, + namespaceType: 'multiple', + title: 'saved_objects*', }, - namespaceType: 'multiple', - title: 'saved_objects*', - }, - relationship: 'child', - type: 'index-pattern', - }); - expect(relations2).to.eql({ - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - relationship: 'parent', + relationship: 'child', + type: 'index-pattern', + }); + expect(relations2).to.eql({ + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + type: 'visualization', + meta: { + icon: 'visualizeApp', + title: 'VisualizationFromSavedSearch', + namespaceType: 'multiple-isolated', + hiddenType: false, + }, + relationship: 'parent', + }); }); - }); - it('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get( - relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) - ) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).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', + it('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get( + relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) + ) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).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, }, - namespaceType: 'multiple', - hiddenType: false, - }, - relationship: 'child', - }); - expect(relations2).to.eql({ - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - meta: { - hiddenType: false, - icon: 'visualizeApp', - namespaceType: 'multiple-isolated', - title: 'VisualizationFromSavedSearch', - }, - relationship: 'parent', - type: 'visualization', + relationship: 'child', + }); + expect(relations2).to.eql({ + id: 'a42c0580-3224-11e8-a572-ffca06da1357', + meta: { + hiddenType: false, + icon: 'visualizeApp', + namespaceType: 'multiple-isolated', + title: 'VisualizationFromSavedSearch', + }, + relationship: 'parent', + type: 'visualization', + }); }); - }); - - it('should return 404 if search finds no results', async () => { - await supertest - .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); - }); - }); - describe('dashboards', () => { - before(async () => { - 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(); - }); - - it('should validate dashboard response schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - - expect(() => { - relationSchema.validate(relations1); - }).not.to.throwException(); + it('should return 404 if search finds no results', async () => { + await supertest + .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); }); - it.skip('should work for dashboards', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); + describe('dashboards', () => { + it('should validate dashboard relationships schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + + expect(() => { + relationSchema.validate(body.relations[0]); + }).not.to.throwError(); + }); - expect(body.relations).to.eql([ - { + it('should work for dashboards', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).to.eql({ id: 'add810b0-3224-11e8-a572-ffca06da1357', type: 'visualization', relationship: 'child', meta: { icon: 'visualizeApp', title: 'Visualization', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, namespaceType: 'multiple-isolated', hiddenType: false, }, - }, - { + }); + expect(relations2).to.eql({ id: 'a42c0580-3224-11e8-a572-ffca06da1357', type: 'visualization', relationship: 'child', meta: { icon: 'visualizeApp', title: 'VisualizationFromSavedSearch', - inAppUrl: { - path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, namespaceType: 'multiple-isolated', hiddenType: false, }, - }, - ]); - }); - - it.skip('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); + }); + }); - expect(body.relations).to.eql([ - { + it('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).to.eql({ id: 'add810b0-3224-11e8-a572-ffca06da1357', type: 'visualization', meta: { icon: 'visualizeApp', title: 'Visualization', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, namespaceType: 'multiple-isolated', hiddenType: false, }, relationship: 'child', - }, - { + }); + expect(relations2).to.eql({ id: 'a42c0580-3224-11e8-a572-ffca06da1357', type: 'visualization', meta: { icon: 'visualizeApp', title: 'VisualizationFromSavedSearch', - inAppUrl: { - path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, namespaceType: 'multiple-isolated', hiddenType: false, }, relationship: 'child', - }, - ]); - }); - - it.skip('should return 404 if dashboard finds no results', async () => { - await supertest - .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); - }); - }); + }); + }); - describe.skip('visualizations', () => { - it('should validate visualization response schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(() => { - responseSchema.validate(body); - }).not.to.throwError(); + it('should return 404 if dashboard finds no results', async () => { + await supertest + .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); }); - it('should work for visualizations', async () => { - const { body } = await supertest - .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); + describe('visualizations', () => { + it('should validate visualization relationships schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + relationSchema.validate(body.relations[0]); + }).not.to.throwError(); + }); - expect(body.relations).to.eql([ - { + it('should work for visualizations', async () => { + const { body } = await supertest + .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).to.eql({ id: '960372e0-3224-11e8-a572-ffca06da1357', type: 'search', relationship: 'child', @@ -332,8 +298,8 @@ export default function ({ getService }: FtrProviderContext) { namespaceType: 'multiple-isolated', hiddenType: false, }, - }, - { + }); + expect(relations2).to.eql({ id: 'b70c7ae0-3224-11e8-a572-ffca06da1357', type: 'dashboard', relationship: 'parent', @@ -347,21 +313,20 @@ export default function ({ getService }: FtrProviderContext) { namespaceType: 'multiple-isolated', hiddenType: false, }, - }, - ]); - }); + }); + }); - it('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get( - relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) - ) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(body.relations).to.eql([ - { + it('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get( + relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) + ) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + + expect(relations1).to.eql({ id: '960372e0-3224-11e8-a572-ffca06da1357', type: 'search', meta: { @@ -375,41 +340,40 @@ export default function ({ getService }: FtrProviderContext) { hiddenType: false, }, relationship: 'child', - }, - ]); - }); - - it('should return 404 if visualizations finds no results', async () => { - await supertest - .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); - }); - }); + }); + }); - describe.skip('index patterns', () => { - it('should validate visualization response schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(() => { - responseSchema.validate(body); - }).not.to.throwError(); + it('should return 404 if visualizations finds no results', async () => { + await supertest + .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); }); - it('should work for index patterns', async () => { - const { body } = await supertest - .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); + describe('index patterns', () => { + it('should validate visualization relationships schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + relationSchema.validate(body.relations[0]); + }).not.to.throwError(); + }); - expect(body.relations).to.eql([ - { + it('should work for index patterns', async () => { + const { body } = await supertest + .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + relations2 = body.relations[1]; + expect(relations1).to.eql({ id: '960372e0-3224-11e8-a572-ffca06da1357', type: 'search', relationship: 'parent', @@ -423,36 +387,30 @@ export default function ({ getService }: FtrProviderContext) { namespaceType: 'multiple-isolated', hiddenType: false, }, - }, - { + }); + expect(relations2).to.eql({ id: 'add810b0-3224-11e8-a572-ffca06da1357', type: 'visualization', relationship: 'parent', meta: { icon: 'visualizeApp', title: 'Visualization', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, namespaceType: 'multiple-isolated', hiddenType: false, }, - }, - ]); - }); + }); + }); - it('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get( - relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) - ) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(body.relations).to.eql([ - { + it('should filter based on savedObjectTypes', async () => { + const { body } = await supertest + .get( + relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) + ) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + relations1 = body.relations[0]; + expect(relations1).to.eql({ id: '960372e0-3224-11e8-a572-ffca06da1357', type: 'search', meta: { @@ -466,64 +424,61 @@ export default function ({ getService }: FtrProviderContext) { hiddenType: false, }, relationship: 'parent', - }, - ]); - }); + }); + }); - it('should return 404 if index pattern finds no results', async () => { - await supertest - .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); + it('should return 404 if index pattern finds no results', async () => { + await supertest + .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(404); + }); }); - }); - describe.skip('invalid references', () => { - it('should validate the response schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'invalid-refs')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(() => { - responseSchema.validate(body); - }).not.to.throwError(); - }); + describe('invalid references', () => { + it('should validate the relationships schema', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'invalid-refs')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + relationSchema.validate(body.relations[0]); + }).not.to.throwError(); + expect(() => { + invalidRelationSchema.validate(body.invalidRelations[0]); + }).not.to.throwError(); + }); - it('should return the invalid relations', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'invalid-refs')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); + it('should return the invalid relations', async () => { + const { body } = await supertest + .get(relationshipsUrl('dashboard', 'invalid-refs')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + const invalidRelation = body.invalidRelations[0]; + relations1 = body.relations[0]; - expect(body.invalidRelations).to.eql([ - { + expect(invalidRelation).to.eql({ error: 'Saved object [visualization/invalid-vis] not found', id: 'invalid-vis', relationship: 'child', type: 'visualization', - }, - ]); - expect(body.relations).to.eql([ - { + }); + expect(relations1).to.eql({ id: 'add810b0-3224-11e8-a572-ffca06da1357', meta: { icon: 'visualizeApp', - inAppUrl: { - path: '/app/visualize#/edit/add810b0-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'visualize.show', - }, namespaceType: 'multiple-isolated', hiddenType: false, title: 'Visualization', }, relationship: 'child', type: 'visualization', - }, - ]); + }); + }); }); }); }); From 58e6f133b45e55ee49ffc00d85aa26673e7c29ff Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 7 Jun 2024 12:33:45 +0200 Subject: [PATCH 12/14] refactor before and after hooks to be one level shallower :shrug: --- .../common/saved_objects_management/find.ts | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts index c4bd5ef95fb41..b06b2176bc333 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/find.ts @@ -24,7 +24,7 @@ export default function ({ getService }: FtrProviderContext) { await svlUserManager.invalidateApiKeyForRole(roleAuthc); }); - describe('with kibana index', () => { + describe('with kibana index - basic', () => { before(async () => { await kibanaServer.savedObjects.cleanStandardList(); await kibanaServer.importExport.load( @@ -96,20 +96,27 @@ export default function ({ getService }: FtrProviderContext) { }); }); }); + }); + 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', () => { - before(async () => { - await kibanaServer.savedObjects.cleanStandardList(); - 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' - ); - }); - it('search for a reference', async () => { const { body } = await supertest .get('/api/kibana/management/saved_objects/_find') @@ -124,8 +131,8 @@ export default function ({ getService }: FtrProviderContext) { expect(objects.map((obj: any) => obj.id)).to.eql(['only-ref-1', 'ref-1-and-ref-2']); }); }); - // does not work in serverless mode - it.skip('search for multiple references with OR operator', async () => { + + it('search for multiple references with OR operator', async () => { await supertest .get('/api/kibana/management/saved_objects/_find') .query({ @@ -134,7 +141,7 @@ export default function ({ getService }: FtrProviderContext) { { type: 'ref-type', id: 'ref-1' }, { type: 'ref-type', id: 'ref-2' }, ]), - // hasReferenceOperator: 'OR', + hasReferenceOperator: 'OR', }) .set(svlCommonApi.getInternalRequestHeader()) .set(roleAuthc.apiKeyHeader) @@ -146,8 +153,8 @@ export default function ({ getService }: FtrProviderContext) { ); }); }); - // does not work in serverless mode - it.skip('search for multiple references with AND operator', async () => { + + it('search for multiple references with AND operator', async () => { const { body } = await supertest .get('/api/kibana/management/saved_objects/_find') .query({ @@ -165,8 +172,7 @@ export default function ({ getService }: FtrProviderContext) { expect(objects.map((obj: any) => obj.id)).to.eql(['ref-1-and-ref-2']); }); - describe.skip('`sortField` and `sortOrder` parameters', () => { - // does not work in serverless mode + describe('`sortField` and `sortOrder` parameters', () => { it('sort objects by "type" in "asc" order', async () => { const { body } = await supertest .get('/api/kibana/management/saved_objects/_find') From c4b348fbb28c34bab8c9c25c9bbd712dea01a1dc Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Fri, 7 Jun 2024 16:22:38 -0700 Subject: [PATCH 13/14] fix reponse schema validation --- .../saved_objects_management/relationships.ts | 985 ++++++++++-------- 1 file changed, 561 insertions(+), 424 deletions(-) diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts index 194fa0997baf2..cb2a739ea6fb9 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts @@ -7,12 +7,10 @@ import expect from '@kbn/expect'; import { schema } from '@kbn/config-schema'; -import { SavedObjectRelation } from '@kbn/saved-objects-management-plugin/public'; +// import { SavedObjectRelation } from '@kbn/saved-objects-management-plugin/public'; import { FtrProviderContext } from '../../../ftr_provider_context'; import { RoleCredentials } from '../../../../shared/services'; -// TODO: convert to supertestWithoutAuth - export default function ({ getService }: FtrProviderContext) { const svlCommonApi = getService('svlCommonApi'); const svlUserManager = getService('svlUserManager'); @@ -45,441 +43,580 @@ export default function ({ getService }: FtrProviderContext) { 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', () => { - 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}`; - }; - let relations1: SavedObjectRelation; - let relations2: SavedObjectRelation; - 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); }); - describe('defaultTypes relations', () => { - before(async () => { - 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(); - }); - describe('searches', () => { - it('should validate search relationships schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - expect(() => { - relationSchema.validate(body.relations[0]); - }).not.to.throwError(); - }); - - it('should work for searches', async () => { - const { body } = await supertest - .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).to.eql({ - id: '8963ca30-3224-11e8-a572-ffca06da1357', - meta: { - editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', - hiddenType: false, - icon: 'indexPatternApp', - inAppUrl: { - path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', - uiCapabilitiesPath: 'management.kibana.indexPatterns', - }, - namespaceType: 'multiple', - title: 'saved_objects*', - }, - relationship: 'child', - type: 'index-pattern', - }); - expect(relations2).to.eql({ - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - relationship: 'parent', - }); - }); - - it('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get( - relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) - ) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).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', - }); - expect(relations2).to.eql({ - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - meta: { - hiddenType: false, - icon: 'visualizeApp', - namespaceType: 'multiple-isolated', - title: 'VisualizationFromSavedSearch', - }, - relationship: 'parent', - type: 'visualization', - }); - }); - - it('should return 404 if search finds no results', async () => { - await supertest - .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); - }); - }); + const baseApiUrl = `/api/kibana/management/saved_objects/relationships`; + const defaultTypes = ['visualization', 'index-pattern', 'search', 'dashboard']; - describe('dashboards', () => { - it('should validate dashboard relationships schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - - expect(() => { - relationSchema.validate(body.relations[0]); - }).not.to.throwError(); - }); - - it('should work for dashboards', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).to.eql({ - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'child', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - }); - expect(relations2).to.eql({ - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'child', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - }); - }); - - it('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).to.eql({ - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - relationship: 'child', - }); - expect(relations2).to.eql({ - id: 'a42c0580-3224-11e8-a572-ffca06da1357', - type: 'visualization', - meta: { - icon: 'visualizeApp', - title: 'VisualizationFromSavedSearch', - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - relationship: 'child', - }); - }); - - it('should return 404 if dashboard finds no results', async () => { - await supertest - .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); - }); - }); + const relationshipsUrl = (type: string, id: string, types: string[] = defaultTypes) => { + const typesQuery = types.map((t) => `savedObjectTypes=${t}`).join('&'); + return `${baseApiUrl}/${type}/${id}?${typesQuery}`; + }; - describe('visualizations', () => { - it('should validate visualization relationships schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(() => { - relationSchema.validate(body.relations[0]); - }).not.to.throwError(); - }); - - it('should work for visualizations', async () => { - const { body } = await supertest - .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).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, - }, - }); - expect(relations2).to.eql({ - 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('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get( - relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) - ) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - - expect(relations1).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('should return 404 if visualizations finds no results', async () => { - await supertest - .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); - }); + describe('validate response schema', () => { + it('search', async () => { + const resp = await supertest + .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); }); - - describe('index patterns', () => { - it('should validate visualization relationships schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(() => { - relationSchema.validate(body.relations[0]); - }).not.to.throwError(); - }); - - it('should work for index patterns', async () => { - const { body } = await supertest - .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - relations2 = body.relations[1]; - expect(relations1).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, - }, - }); - expect(relations2).to.eql({ - id: 'add810b0-3224-11e8-a572-ffca06da1357', - type: 'visualization', - relationship: 'parent', - meta: { - icon: 'visualizeApp', - title: 'Visualization', - namespaceType: 'multiple-isolated', - hiddenType: false, - }, - }); - }); - - it('should filter based on savedObjectTypes', async () => { - const { body } = await supertest - .get( - relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) - ) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - relations1 = body.relations[0]; - expect(relations1).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', - }); - }); - - it('should return 404 if index pattern finds no results', async () => { - await supertest - .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(404); - }); + it('dashboard', async () => { + const resp = await supertest + .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + .set(svlCommonApi.getInternalRequestHeader()) + .set(roleAuthc.apiKeyHeader) + .expect(200); + + expect(() => { + responseSchema.validate(resp.body); + }).not.to.throwError(); }); - - describe('invalid references', () => { - it('should validate the relationships schema', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'invalid-refs')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - - expect(() => { - relationSchema.validate(body.relations[0]); - }).not.to.throwError(); - expect(() => { - invalidRelationSchema.validate(body.invalidRelations[0]); - }).not.to.throwError(); - }); - - it('should return the invalid relations', async () => { - const { body } = await supertest - .get(relationshipsUrl('dashboard', 'invalid-refs')) - .set(svlCommonApi.getInternalRequestHeader()) - .set(roleAuthc.apiKeyHeader) - .expect(200); - const invalidRelation = body.invalidRelations[0]; - relations1 = body.relations[0]; - - expect(invalidRelation).to.eql({ - error: 'Saved object [visualization/invalid-vis] not found', - id: 'invalid-vis', - relationship: 'child', - type: 'visualization', - }); - expect(relations1).to.eql({ - id: 'add810b0-3224-11e8-a572-ffca06da1357', - meta: { - icon: 'visualizeApp', - namespaceType: 'multiple-isolated', - hiddenType: false, - title: 'Visualization', - }, - relationship: 'child', - type: 'visualization', - }); - }); + it('visualization', async () => { + const resp = await supertest + .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('should work for searches', async () => { + // const resp = await supertest + // .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + // .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', + // inAppUrl: { + // path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + // uiCapabilitiesPath: 'visualize.show', + // }, + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // }, + // ]); + // }); + + // it('should filter based on savedObjectTypes', async () => { + // const resp = await supertest + // .get( + // relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) + // ) + // .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', + // inAppUrl: { + // path: '/app/visualize#/edit/a42c0580-3224-11e8-a572-ffca06da1357', + // uiCapabilitiesPath: 'visualize.show', + // }, + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // relationship: 'parent', + // }, + // ]); + // }); + + // it('should return 404 if search finds no results', async () => { + // await supertest + // .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + // .expect(404); + // }); + // }); + // let relations1: SavedObjectRelation; + // let relations2: SavedObjectRelation; + + // describe('defaultTypes relations', () => { + // before(async () => { + // 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(); + // }); + // describe('searches', () => { + // it('should validate search relationships schema', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // expect(() => { + // relationSchema.validate(body.relations[0]); + // }).not.to.throwError(); + // }); + + // it('should work for searches', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // relations2 = body.relations[1]; + // expect(relations1).to.eql({ + // id: '8963ca30-3224-11e8-a572-ffca06da1357', + // meta: { + // editUrl: '/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', + // hiddenType: false, + // icon: 'indexPatternApp', + // inAppUrl: { + // path: '/app/management/kibana/dataViews/dataView/8963ca30-3224-11e8-a572-ffca06da1357', + // uiCapabilitiesPath: 'management.kibana.indexPatterns', + // }, + // namespaceType: 'multiple', + // title: 'saved_objects*', + // }, + // relationship: 'child', + // type: 'index-pattern', + // }); + // expect(relations2).to.eql({ + // id: 'a42c0580-3224-11e8-a572-ffca06da1357', + // type: 'visualization', + // meta: { + // icon: 'visualizeApp', + // title: 'VisualizationFromSavedSearch', + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // relationship: 'parent', + // }); + // }); + + // it('should filter based on savedObjectTypes', async () => { + // const { body } = await supertest + // .get( + // relationshipsUrl('search', '960372e0-3224-11e8-a572-ffca06da1357', ['visualization']) + // ) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // relations2 = body.relations[1]; + // expect(relations1).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', + // }); + // expect(relations2).to.eql({ + // id: 'a42c0580-3224-11e8-a572-ffca06da1357', + // meta: { + // hiddenType: false, + // icon: 'visualizeApp', + // namespaceType: 'multiple-isolated', + // title: 'VisualizationFromSavedSearch', + // }, + // relationship: 'parent', + // type: 'visualization', + // }); + // }); + + // it('should return 404 if search finds no results', async () => { + // await supertest + // .get(relationshipsUrl('search', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(404); + // }); + // }); + + // describe('dashboards', () => { + // it('should validate dashboard relationships schema', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + + // expect(() => { + // relationSchema.validate(body.relations[0]); + // }).not.to.throwError(); + // }); + + // it('should work for dashboards', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // relations2 = body.relations[1]; + // expect(relations1).to.eql({ + // id: 'add810b0-3224-11e8-a572-ffca06da1357', + // type: 'visualization', + // relationship: 'child', + // meta: { + // icon: 'visualizeApp', + // title: 'Visualization', + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // }); + // expect(relations2).to.eql({ + // id: 'a42c0580-3224-11e8-a572-ffca06da1357', + // type: 'visualization', + // relationship: 'child', + // meta: { + // icon: 'visualizeApp', + // title: 'VisualizationFromSavedSearch', + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // }); + // }); + + // it('should filter based on savedObjectTypes', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('dashboard', 'b70c7ae0-3224-11e8-a572-ffca06da1357', ['search'])) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // relations2 = body.relations[1]; + // expect(relations1).to.eql({ + // id: 'add810b0-3224-11e8-a572-ffca06da1357', + // type: 'visualization', + // meta: { + // icon: 'visualizeApp', + // title: 'Visualization', + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // relationship: 'child', + // }); + // expect(relations2).to.eql({ + // id: 'a42c0580-3224-11e8-a572-ffca06da1357', + // type: 'visualization', + // meta: { + // icon: 'visualizeApp', + // title: 'VisualizationFromSavedSearch', + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // relationship: 'child', + // }); + // }); + + // it('should return 404 if dashboard finds no results', async () => { + // await supertest + // .get(relationshipsUrl('dashboard', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(404); + // }); + // }); + + // describe('visualizations', () => { + // it('should validate visualization relationships schema', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + + // expect(() => { + // relationSchema.validate(body.relations[0]); + // }).not.to.throwError(); + // }); + + // it('should work for visualizations', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // relations2 = body.relations[1]; + // expect(relations1).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, + // }, + // }); + // expect(relations2).to.eql({ + // 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('should filter based on savedObjectTypes', async () => { + // const { body } = await supertest + // .get( + // relationshipsUrl('visualization', 'a42c0580-3224-11e8-a572-ffca06da1357', ['search']) + // ) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + + // expect(relations1).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('should return 404 if visualizations finds no results', async () => { + // await supertest + // .get(relationshipsUrl('visualization', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(404); + // }); + // }); + + // describe('index patterns', () => { + // it('should validate visualization relationships schema', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + + // expect(() => { + // relationSchema.validate(body.relations[0]); + // }).not.to.throwError(); + // }); + + // it('should work for index patterns', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // relations2 = body.relations[1]; + // expect(relations1).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, + // }, + // }); + // expect(relations2).to.eql({ + // id: 'add810b0-3224-11e8-a572-ffca06da1357', + // type: 'visualization', + // relationship: 'parent', + // meta: { + // icon: 'visualizeApp', + // title: 'Visualization', + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // }, + // }); + // }); + + // it('should filter based on savedObjectTypes', async () => { + // const { body } = await supertest + // .get( + // relationshipsUrl('index-pattern', '8963ca30-3224-11e8-a572-ffca06da1357', ['search']) + // ) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // relations1 = body.relations[0]; + // expect(relations1).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', + // }); + // }); + + // it('should return 404 if index pattern finds no results', async () => { + // await supertest + // .get(relationshipsUrl('index-pattern', 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(404); + // }); + // }); + + // describe('invalid references', () => { + // it('should validate the relationships schema', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('dashboard', 'invalid-refs')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + + // expect(() => { + // relationSchema.validate(body.relations[0]); + // }).not.to.throwError(); + // expect(() => { + // invalidRelationSchema.validate(body.invalidRelations[0]); + // }).not.to.throwError(); + // }); + + // it('should return the invalid relations', async () => { + // const { body } = await supertest + // .get(relationshipsUrl('dashboard', 'invalid-refs')) + // .set(svlCommonApi.getInternalRequestHeader()) + // .set(roleAuthc.apiKeyHeader) + // .expect(200); + // const invalidRelation = body.invalidRelations[0]; + // relations1 = body.relations[0]; + + // expect(invalidRelation).to.eql({ + // error: 'Saved object [visualization/invalid-vis] not found', + // id: 'invalid-vis', + // relationship: 'child', + // type: 'visualization', + // }); + // expect(relations1).to.eql({ + // id: 'add810b0-3224-11e8-a572-ffca06da1357', + // meta: { + // icon: 'visualizeApp', + // namespaceType: 'multiple-isolated', + // hiddenType: false, + // title: 'Visualization', + // }, + // relationship: 'child', + // type: 'visualization', + // }); + // }); + // }); + // }); + // }); } From ae19027e56597cdd3782619084d560b390a248c1 Mon Sep 17 00:00:00 2001 From: "Christiane (Tina) Heiligers" Date: Fri, 7 Jun 2024 16:26:21 -0700 Subject: [PATCH 14/14] Validate response schema --- .../apis/saved_objects_management/relationships.ts | 2 +- .../common/saved_objects_management/relationships.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/test/api_integration/apis/saved_objects_management/relationships.ts b/test/api_integration/apis/saved_objects_management/relationships.ts index d609938a0d50f..dda99c138ebb8 100644 --- a/test/api_integration/apis/saved_objects_management/relationships.ts +++ b/test/api_integration/apis/saved_objects_management/relationships.ts @@ -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); diff --git a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts index cb2a739ea6fb9..ab320dbc801f7 100644 --- a/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts +++ b/x-pack/test_serverless/api_integration/test_suites/common/saved_objects_management/relationships.ts @@ -108,6 +108,17 @@ export default function ({ getService }: FtrProviderContext) { responseSchema.validate(resp.body); }).not.to.throwError(); }); + it('index-pattern', async () => { + const resp = await supertest + .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(); + }); }); });