mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Spaces] Content summary API (#182921)
## Summary Added space content summary `GET` API endpoint which returns summary of the different saved objects that exist within each space. Request `GET kbn:/internal/spaces/a-space/content_summary` Response ``` { "summary": [ { "count": 1, "type": "config", "displayName": "Config" }, { "count": 2, "type": "dashboard", "displayName": "Dashboard", "icon": "dashboardApp" }, { "count": 4, "type": "index-pattern", "displayName": "data view", "icon": "indexPatternApp" }, { "count": 2, "type": "tag", "displayName": "Tag", "icon": "tag" } ], "total": 9 } ``` Request `GET kbn:/internal/spaces/not-a-space/content_summary` Response ``` { "statusCode": 404, "error": "Not Found", "message": "Not Found" } ``` ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [x] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed. [Report](https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/5903) ### For maintainers - [x] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) __Fixes: https://github.com/elastic/kibana/issues/182406__
This commit is contained in:
parent
e61faa64ba
commit
a48646cc96
5 changed files with 679 additions and 0 deletions
|
@ -0,0 +1,414 @@
|
|||
/*
|
||||
* 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 { capitalize } from 'lodash';
|
||||
import * as Rx from 'rxjs';
|
||||
|
||||
import type { SavedObjectsNamespaceType, SavedObjectsType } from '@kbn/core/server';
|
||||
import { kibanaResponseFactory } from '@kbn/core/server';
|
||||
import {
|
||||
coreMock,
|
||||
httpServerMock,
|
||||
httpServiceMock,
|
||||
savedObjectsClientMock,
|
||||
savedObjectsTypeRegistryMock,
|
||||
} from '@kbn/core/server/mocks';
|
||||
|
||||
import type { SpaceContentTypeSummaryItem } from './get_content_summary';
|
||||
import { initGetSpaceContentSummaryApi } from './get_content_summary';
|
||||
import { spacesConfig } from '../../../lib/__fixtures__';
|
||||
import { SpacesClientService } from '../../../spaces_client';
|
||||
import { SpacesService } from '../../../spaces_service';
|
||||
import {
|
||||
createMockSavedObjectsRepository,
|
||||
createSpaces,
|
||||
mockRouteContext,
|
||||
mockRouteContextWithInvalidLicense,
|
||||
} from '../__fixtures__';
|
||||
|
||||
interface SetupParams {
|
||||
importableAndExportableTypesMock: SavedObjectsType[];
|
||||
}
|
||||
|
||||
describe('GET /internal/spaces/{spaceId}/content_summary', () => {
|
||||
const spacesSavedObjects = createSpaces();
|
||||
|
||||
const setup = async (params?: SetupParams) => {
|
||||
const httpService = httpServiceMock.createSetupContract();
|
||||
const router = httpServiceMock.createRouter();
|
||||
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
const savedObjectsRepositoryMock = createMockSavedObjectsRepository(spacesSavedObjects);
|
||||
|
||||
const clientService = new SpacesClientService(jest.fn());
|
||||
clientService
|
||||
.setup({ config$: Rx.of(spacesConfig) })
|
||||
.setClientRepositoryFactory(() => savedObjectsRepositoryMock);
|
||||
|
||||
const savedObjectsClient = savedObjectsClientMock.create();
|
||||
const typeRegistry = savedObjectsTypeRegistryMock.create();
|
||||
|
||||
typeRegistry.getImportableAndExportableTypes.mockReturnValue(
|
||||
params?.importableAndExportableTypesMock ?? [
|
||||
// don't need to include all types, just need a positive case (agnostic) and a negative case (non-agnostic)
|
||||
{
|
||||
name: 'dashboard',
|
||||
namespaceType: 'multiple',
|
||||
hidden: false,
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'globaltype',
|
||||
namespaceType: 'agnostic',
|
||||
hidden: false,
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
]
|
||||
);
|
||||
typeRegistry.isNamespaceAgnostic.mockImplementation((type: string) =>
|
||||
typeRegistry
|
||||
.getImportableAndExportableTypes()
|
||||
.some((t) => t.name === type && t.namespaceType === 'agnostic')
|
||||
);
|
||||
|
||||
const service = new SpacesService();
|
||||
service.setup({
|
||||
basePath: httpService.basePath,
|
||||
});
|
||||
|
||||
const clientServiceStart = clientService.start(coreStart);
|
||||
|
||||
const spacesServiceStart = service.start({
|
||||
basePath: coreStart.http.basePath,
|
||||
spacesClientService: clientServiceStart,
|
||||
});
|
||||
|
||||
const routeContext = {
|
||||
...mockRouteContext,
|
||||
core: {
|
||||
savedObjects: {
|
||||
getClient: () => savedObjectsClient,
|
||||
typeRegistry,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
initGetSpaceContentSummaryApi({
|
||||
router,
|
||||
getSpacesService: () => spacesServiceStart,
|
||||
});
|
||||
|
||||
const [[config, routeHandler]] = router.get.mock.calls;
|
||||
|
||||
return {
|
||||
config,
|
||||
routeHandler,
|
||||
savedObjectsClient,
|
||||
typeRegistry,
|
||||
routeContext,
|
||||
};
|
||||
};
|
||||
|
||||
it('correctly defines route.', async () => {
|
||||
const { config } = await setup();
|
||||
|
||||
const paramsSchema = (config.validate as any).params;
|
||||
|
||||
expect(config.options).toEqual({ tags: ['access:manageSpaces'] });
|
||||
expect(() => paramsSchema.validate({})).toThrowErrorMatchingInlineSnapshot(
|
||||
`"[spaceId]: expected value of type [string] but got [undefined]"`
|
||||
);
|
||||
expect(() => paramsSchema.validate({ spaceId: '' })).toThrowErrorMatchingInlineSnapshot(
|
||||
`"[spaceId]: value has length [0] but it must have a minimum length of [1]."`
|
||||
);
|
||||
expect(() => paramsSchema.validate({ spaceId: '*' })).toThrowErrorMatchingInlineSnapshot(
|
||||
`"[spaceId]: lower case, a-z, 0-9, \\"_\\", and \\"-\\" are allowed."`
|
||||
);
|
||||
});
|
||||
|
||||
it('returns http/403 when the license is invalid.', async () => {
|
||||
const { routeHandler } = await setup();
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
const response = await routeHandler(
|
||||
mockRouteContextWithInvalidLicense,
|
||||
request,
|
||||
kibanaResponseFactory
|
||||
);
|
||||
|
||||
expect(response.status).toEqual(403);
|
||||
expect(response.payload).toEqual({
|
||||
message: 'License is invalid for spaces',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns http/404 when retrieving a non-existent space.', async () => {
|
||||
const { routeHandler, routeContext } = await setup();
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
params: {
|
||||
spaceId: 'not-a-space',
|
||||
},
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
const response = await routeHandler(routeContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(response.status).toEqual(404);
|
||||
});
|
||||
|
||||
it('returns http/200 with non agnostic namespace types.', async () => {
|
||||
const importableAndExportableTypesMock = [
|
||||
{
|
||||
name: 'dashboard',
|
||||
namespaceType: 'multiple' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
management: {
|
||||
displayName: 'dashboardDisplayName',
|
||||
icon: 'dashboardIcon',
|
||||
},
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'query',
|
||||
namespaceType: 'multiple' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'globaltype',
|
||||
namespaceType: 'agnostic' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
];
|
||||
const { routeHandler, routeContext, savedObjectsClient } = await setup({
|
||||
importableAndExportableTypesMock,
|
||||
});
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
params: {
|
||||
spaceId: 'a-space',
|
||||
},
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
const mockAggregationResult = {
|
||||
total: 6,
|
||||
aggregations: {
|
||||
typesAggregation: {
|
||||
buckets: [
|
||||
{
|
||||
key: 'dashboard',
|
||||
doc_count: 5,
|
||||
},
|
||||
{
|
||||
key: 'query',
|
||||
doc_count: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const findMock = savedObjectsClient.find as jest.Mock;
|
||||
|
||||
findMock.mockReturnValue(mockAggregationResult);
|
||||
|
||||
const response = await routeHandler(routeContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(findMock).toBeCalledWith({
|
||||
type: ['dashboard', 'query'],
|
||||
namespaces: ['a-space'],
|
||||
perPage: 0,
|
||||
aggs: {
|
||||
typesAggregation: {
|
||||
terms: {
|
||||
field: 'type',
|
||||
size: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload?.summary).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('returns http/200 with correct meta information.', async () => {
|
||||
const importableAndExportableTypesMock = [
|
||||
{
|
||||
name: 'dashboard',
|
||||
namespaceType: 'multiple' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
management: {
|
||||
displayName: 'dashboardDisplayName',
|
||||
icon: 'dashboardIcon',
|
||||
},
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'query',
|
||||
namespaceType: 'multiple' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
];
|
||||
const { routeHandler, routeContext, savedObjectsClient } = await setup({
|
||||
importableAndExportableTypesMock,
|
||||
});
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
params: {
|
||||
spaceId: 'a-space',
|
||||
},
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
const mockAggregationResult = {
|
||||
total: 10,
|
||||
aggregations: {
|
||||
typesAggregation: {
|
||||
buckets: [
|
||||
{
|
||||
key: 'dashboard',
|
||||
doc_count: 5,
|
||||
},
|
||||
{
|
||||
key: 'query',
|
||||
doc_count: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const findMock = savedObjectsClient.find as jest.Mock;
|
||||
|
||||
findMock.mockReturnValue(mockAggregationResult);
|
||||
|
||||
const response = await routeHandler(routeContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(findMock).toBeCalledWith({
|
||||
type: ['dashboard', 'query'],
|
||||
namespaces: ['a-space'],
|
||||
perPage: 0,
|
||||
aggs: {
|
||||
typesAggregation: {
|
||||
terms: {
|
||||
field: 'type',
|
||||
size: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload!.summary).toHaveLength(2);
|
||||
|
||||
const [dashboardType, queryType] = importableAndExportableTypesMock;
|
||||
const [dashboardTypeSummary, queryTypeSummary] = response.payload!.summary;
|
||||
|
||||
expect(dashboardTypeSummary.displayName).toEqual(dashboardType.management?.displayName);
|
||||
expect(dashboardTypeSummary.icon).toEqual(dashboardType.management?.icon);
|
||||
|
||||
expect(queryTypeSummary.displayName).toEqual(capitalize(queryType.name));
|
||||
expect(queryTypeSummary.icon).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns http/200 with data sorted by displayName.', async () => {
|
||||
const importableAndExportableTypesMock = [
|
||||
{
|
||||
name: 'dashboard',
|
||||
namespaceType: 'multiple' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
management: {
|
||||
displayName: 'Test Display Dashboard Name',
|
||||
},
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'query',
|
||||
namespaceType: 'multiple' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
management: {
|
||||
displayName: 'My Display Name',
|
||||
},
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
{
|
||||
name: 'search',
|
||||
namespaceType: 'multiple' as SavedObjectsNamespaceType,
|
||||
hidden: false,
|
||||
mappings: { properties: {} },
|
||||
},
|
||||
];
|
||||
const { routeHandler, routeContext, savedObjectsClient } = await setup({
|
||||
importableAndExportableTypesMock,
|
||||
});
|
||||
|
||||
const request = httpServerMock.createKibanaRequest({
|
||||
params: {
|
||||
spaceId: 'a-space',
|
||||
},
|
||||
method: 'get',
|
||||
});
|
||||
|
||||
const mockAggregationResult = {
|
||||
total: 15,
|
||||
aggregations: {
|
||||
typesAggregation: {
|
||||
buckets: [
|
||||
{
|
||||
key: 'dashboard',
|
||||
doc_count: 5,
|
||||
},
|
||||
{
|
||||
key: 'query',
|
||||
doc_count: 5,
|
||||
},
|
||||
{
|
||||
key: 'search',
|
||||
doc_count: 5,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const findMock = savedObjectsClient.find as jest.Mock;
|
||||
|
||||
findMock.mockReturnValue(mockAggregationResult);
|
||||
|
||||
const response = await routeHandler(routeContext, request, kibanaResponseFactory);
|
||||
|
||||
expect(findMock).toBeCalledWith({
|
||||
type: ['dashboard', 'query', 'search'],
|
||||
namespaces: ['a-space'],
|
||||
perPage: 0,
|
||||
aggs: {
|
||||
typesAggregation: {
|
||||
terms: {
|
||||
field: 'type',
|
||||
size: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.payload!.summary).toHaveLength(3);
|
||||
|
||||
const types = response.payload!.summary.map((item: SpaceContentTypeSummaryItem) => item.type);
|
||||
|
||||
expect(types).toEqual(['query', 'search', 'dashboard']);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* 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 { capitalize, sortBy } from 'lodash';
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { SavedObjectsErrorHelpers } from '@kbn/core/server';
|
||||
|
||||
import type { InternalRouteDeps } from '.';
|
||||
import { wrapError } from '../../../lib/errors';
|
||||
import { SPACE_ID_REGEX } from '../../../lib/space_schema';
|
||||
import { createLicensedRouteHandler } from '../../lib';
|
||||
|
||||
interface SpaceContentTypeMetaInfo {
|
||||
displayName: string;
|
||||
icon?: string;
|
||||
}
|
||||
|
||||
interface TypesAggregation {
|
||||
typesAggregation: {
|
||||
buckets: Array<{ doc_count: number; key: string }>;
|
||||
};
|
||||
}
|
||||
|
||||
type SpaceContentTypesMetaData = Record<string, SpaceContentTypeMetaInfo>;
|
||||
|
||||
export interface SpaceContentTypeSummaryItem extends SpaceContentTypeMetaInfo {
|
||||
count: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export function initGetSpaceContentSummaryApi(deps: InternalRouteDeps) {
|
||||
const { router, getSpacesService } = deps;
|
||||
|
||||
router.get(
|
||||
{
|
||||
path: '/internal/spaces/{spaceId}/content_summary',
|
||||
options: {
|
||||
tags: ['access:manageSpaces'],
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({
|
||||
spaceId: schema.string({
|
||||
validate: (value) => {
|
||||
if (!SPACE_ID_REGEX.test(value)) {
|
||||
return `lower case, a-z, 0-9, "_", and "-" are allowed.`;
|
||||
}
|
||||
},
|
||||
minLength: 1,
|
||||
}),
|
||||
}),
|
||||
},
|
||||
},
|
||||
createLicensedRouteHandler(async (context, request, response) => {
|
||||
try {
|
||||
const spaceId = request.params.spaceId;
|
||||
const spacesClient = getSpacesService().createSpacesClient(request);
|
||||
|
||||
await spacesClient.get(spaceId);
|
||||
|
||||
const { getClient, typeRegistry } = (await context.core).savedObjects;
|
||||
const client = getClient();
|
||||
|
||||
const types = typeRegistry
|
||||
.getImportableAndExportableTypes()
|
||||
.filter((type) => !typeRegistry.isNamespaceAgnostic(type.name));
|
||||
|
||||
const searchTypeNames = types.map((type) => type.name);
|
||||
|
||||
const data = await client.find<unknown, TypesAggregation>({
|
||||
type: searchTypeNames,
|
||||
perPage: 0,
|
||||
namespaces: [spaceId],
|
||||
aggs: {
|
||||
typesAggregation: {
|
||||
terms: {
|
||||
field: 'type',
|
||||
size: types.length,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const typesMetaInfo = types.reduce<SpaceContentTypesMetaData>((acc, currentType) => {
|
||||
acc[currentType.name] = {
|
||||
displayName: currentType.management?.displayName ?? capitalize(currentType.name),
|
||||
icon: currentType.management?.icon,
|
||||
};
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const summary = sortBy(
|
||||
data.aggregations?.typesAggregation.buckets.map<SpaceContentTypeSummaryItem>((item) => ({
|
||||
count: item.doc_count,
|
||||
type: item.key,
|
||||
...typesMetaInfo[item.key],
|
||||
})),
|
||||
(item) => item.displayName.toLowerCase()
|
||||
);
|
||||
|
||||
return response.ok({ body: { summary, total: data.total } });
|
||||
} catch (error) {
|
||||
if (SavedObjectsErrorHelpers.isNotFoundError(error)) {
|
||||
return response.notFound();
|
||||
}
|
||||
|
||||
return response.customError(wrapError(error));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import { initGetActiveSpaceApi } from './get_active_space';
|
||||
import { initGetSpaceContentSummaryApi } from './get_content_summary';
|
||||
import type { SpacesServiceStart } from '../../../spaces_service/spaces_service';
|
||||
import type { SpacesRouter } from '../../../types';
|
||||
|
||||
|
@ -16,4 +17,5 @@ export interface InternalRouteDeps {
|
|||
|
||||
export function initInternalSpacesApi(deps: InternalRouteDeps) {
|
||||
initGetActiveSpaceApi(deps);
|
||||
initGetSpaceContentSummaryApi(deps);
|
||||
}
|
||||
|
|
147
x-pack/test/api_integration/apis/spaces/get_content_summary.ts
Normal file
147
x-pack/test/api_integration/apis/spaces/get_content_summary.ts
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* 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 sampleDashboard = {
|
||||
contentTypeId: 'dashboard',
|
||||
data: {
|
||||
kibanaSavedObjectMeta: {},
|
||||
title: 'Sample dashboard',
|
||||
},
|
||||
options: {
|
||||
references: [],
|
||||
overwrite: true,
|
||||
},
|
||||
version: 2,
|
||||
};
|
||||
|
||||
const sampleIndexPattern = {
|
||||
contentTypeId: 'index-pattern',
|
||||
data: {
|
||||
fieldAttrs: '{}',
|
||||
title: 'index-pattern-1',
|
||||
timeFieldName: '@timestamp',
|
||||
sourceFilters: '[]',
|
||||
fields: '[]',
|
||||
fieldFormatMap: '{}',
|
||||
typeMeta: '{}',
|
||||
runtimeFieldMap: '{}',
|
||||
name: 'index-pattern-1',
|
||||
},
|
||||
options: { id: 'index-pattern-1' },
|
||||
version: 1,
|
||||
};
|
||||
|
||||
const ATestSpace = 'ab-space';
|
||||
const BTestSpace = 'ac-space';
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
const spacesService = getService('spaces');
|
||||
|
||||
describe('GET /internal/spaces/{spaceId}/content_summary', () => {
|
||||
before(async () => {
|
||||
await spacesService.create({
|
||||
id: ATestSpace,
|
||||
name: 'AB Space',
|
||||
disabledFeatures: [],
|
||||
color: '#AABBCC',
|
||||
});
|
||||
|
||||
await spacesService.create({
|
||||
id: BTestSpace,
|
||||
name: 'AC Space',
|
||||
disabledFeatures: [],
|
||||
color: '#AABBCC',
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await spacesService.delete('ab-space');
|
||||
await spacesService.delete('ac-space');
|
||||
});
|
||||
|
||||
it(`returns content summary for ${ATestSpace} space`, async () => {
|
||||
await supertest
|
||||
.post(`/s/${ATestSpace}/api/content_management/rpc/create`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(sampleDashboard);
|
||||
|
||||
await supertest
|
||||
.post(`/s/${ATestSpace}/api/content_management/rpc/create`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(sampleDashboard);
|
||||
|
||||
await supertest
|
||||
.get(`/internal/spaces/${ATestSpace}/content_summary`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const { summary, total } = response.body;
|
||||
expect(summary).to.eql([
|
||||
{
|
||||
count: 2,
|
||||
type: 'dashboard',
|
||||
displayName: 'Dashboard',
|
||||
icon: 'dashboardApp',
|
||||
},
|
||||
]);
|
||||
expect(total).to.eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
it(`returns content summary for ${BTestSpace} space`, async () => {
|
||||
await supertest
|
||||
.post(`/s/${BTestSpace}/api/content_management/rpc/create`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(sampleDashboard);
|
||||
|
||||
await supertest
|
||||
.post(`/s/${BTestSpace}/api/content_management/rpc/create`)
|
||||
.set('kbn-xsrf', 'foo')
|
||||
.set('x-elastic-internal-origin', 'foo')
|
||||
.send(sampleIndexPattern);
|
||||
|
||||
await supertest
|
||||
.get(`/internal/spaces/${BTestSpace}/content_summary`)
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const { summary, total } = response.body;
|
||||
expect(summary).to.eql([
|
||||
{
|
||||
count: 1,
|
||||
type: 'dashboard',
|
||||
displayName: 'Dashboard',
|
||||
icon: 'dashboardApp',
|
||||
},
|
||||
{
|
||||
count: 1,
|
||||
displayName: 'data view',
|
||||
icon: 'indexPatternApp',
|
||||
type: 'index-pattern',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(total).to.eql(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns 404 when the space is not found', async () => {
|
||||
await supertest
|
||||
.get('/internal/spaces/not-found-space/content_summary')
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(404, {
|
||||
statusCode: 404,
|
||||
error: 'Not Found',
|
||||
message: 'Not Found',
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
|
@ -12,5 +12,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
|
|||
loadTestFile(require.resolve('./get_active_space'));
|
||||
loadTestFile(require.resolve('./saved_objects'));
|
||||
loadTestFile(require.resolve('./space_attributes'));
|
||||
loadTestFile(require.resolve('./get_content_summary'));
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue