mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Infra] Custom dashboards api and saved object redesign (#179576)
Closes #179356 ## Summary This PR changes the custom dashboards' saved object and API from having one saved object per asset type to having one saved object per dashboard. | Before | After | |--------|------| | ``` { dashboardIdList: string[]; assetType: string } ``` | ``` { dashboardId: string; dashboardFilterAssetIdEnabled: boolean; assetType: string; }```| The API endpoints are changed as well to `api/infra/{assetType}/custom-dashboards`: - GET api/infra/host/custom-dashboards - POST api/infra/host/custom-dashboards { "dashboardSavedObjectId": string, "dashboardFilterAssetIdEnabled": boolean } The new endpoints are using `api/infra/{assetType}/custom-dashboards/{id}` where `id` is the id of the saved object - **new** PUT api/infra/host/custom-dashboards/123 - **new** DELETE api/infra/host/custom-dashboards/123 After adding the separate PUT endpoint I added a validation to prevent posting the same dashboard object (payload with the same asset type and dashboard id) twice. So if the POST endpoint is called twice with the same params/payload it will return 400 and a message that the dashboard already exists: "Dashboard with id {id} has already been linked to {asset type}"  Testing: 1. Unit tests - Run in this order in separate terminal tabs/windows - `node scripts/functional_tests_server --config x-pack/test/api_integration/config` - `node scripts/functional_test_runner --config x-pack/test/api_integration/apis/metrics_ui/config.ts --grep 'Infra Custom Dashboards API'` 2. Dev tools - First go to Stack Management > Advanced Settings - Search for "infra" and enable `Custom dashboards for asset details in Infrastructure`: <img width="1704" alt="image" src="614258c8
-ee36-4466-b16c-d48f58c3f5dc"> - Go to Dev Tools and run - POST: ``` POST kbn:/api/infra/host/custom-dashboards { "dashboardSavedObjectId": "123", "dashboardFilterAssetIdEnabled": true } ``` if the same payload is posted 2 times the **second** time the response will be 400 because the dashboard for the current asset already exists: - GET: `GET kbn:/api/infra/host/custom-dashboards` Example Response: ``` [ { "id": "a21acbc4-8102-4b09-8bd1-1a9aa4c36f10", "assetType": "host", "dashboardSavedObjectId": "123", "dashboardFilterAssetIdEnabled": true } ] ``` - PUT (update the returned dashboard): ``` PUT kbn:/api/infra/host/custom-dashboards/a21acbc4-8102-4b09-8bd1-1a9aa4c36f10 { "dashboardSavedObjectId": "123", "dashboardFilterAssetIdEnabled": false } ``` - DELETE (delete the returned dashboard): `DELETE kbn:/api/infra/host/custom-dashboards/a21acbc4-8102-4b09-8bd1-1a9aa4c36f10` --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
8b00070cdf
commit
c9846714f1
15 changed files with 432 additions and 103 deletions
|
@ -176,7 +176,7 @@ export const HASH_TO_VERSION_MAP = {
|
|||
'guided-onboarding-guide-state|a3db59c45a3fd2730816d4f53c35c7d9': '10.0.0',
|
||||
'guided-onboarding-plugin-state|3d1b76c39bfb2cc8296b024d73854724': '10.0.0',
|
||||
'index-pattern|83c02d842fe2a94d14dfa13f7dcd6e87': '10.0.0',
|
||||
'infra-custom-dashboards|6eed22cbe14594bad8c076fa864930de': '10.0.0',
|
||||
'infra-custom-dashboards|1eb3c9e1888b8daea8495769e8d3ba2d': '10.2.0',
|
||||
'infrastructure-monitoring-log-view|c50526fc6040c5355ed027d34d05b35c': '10.0.0',
|
||||
'infrastructure-ui-source|3d1b76c39bfb2cc8296b024d73854724': '10.0.0',
|
||||
'ingest_manager_settings|b91ffb075799c78ffd7dbd51a279c8c9': '10.1.0',
|
||||
|
|
|
@ -462,8 +462,8 @@
|
|||
],
|
||||
"infra-custom-dashboards": [
|
||||
"assetType",
|
||||
"dashboardIdList",
|
||||
"kuery"
|
||||
"dashboardFilterAssetIdEnabled",
|
||||
"dashboardSavedObjectId"
|
||||
],
|
||||
"infrastructure-monitoring-log-view": [
|
||||
"name"
|
||||
|
|
|
@ -1559,15 +1559,16 @@
|
|||
}
|
||||
},
|
||||
"infra-custom-dashboards": {
|
||||
"dynamic": false,
|
||||
"properties": {
|
||||
"assetType": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"dashboardIdList": {
|
||||
"dashboardSavedObjectId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"kuery": {
|
||||
"type": "text"
|
||||
"dashboardFilterAssetIdEnabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -103,7 +103,7 @@ describe('checking migration metadata changes on all registered SO types', () =>
|
|||
"guided-onboarding-guide-state": "d338972ed887ac480c09a1a7fbf582d6a3827c91",
|
||||
"guided-onboarding-plugin-state": "bc109e5ef46ca594fdc179eda15f3095ca0a37a4",
|
||||
"index-pattern": "997108a9ea1e8076e22231e1c95517cdb192b9c5",
|
||||
"infra-custom-dashboards": "b92b6db1c1f8998af6e2951a17b76cf886c6bee5",
|
||||
"infra-custom-dashboards": "1a5994f2e05bb8a1609825ddbf5012f77c5c67f3",
|
||||
"infrastructure-monitoring-log-view": "5f86709d3c27aed7a8379153b08ee5d3d90d77f5",
|
||||
"infrastructure-ui-source": "113182d6895764378dfe7fa9fa027244f3a457c4",
|
||||
"ingest-agent-policies": "7633e578f60c074f8267bc50ec4763845e431437",
|
||||
|
|
|
@ -10,7 +10,11 @@ import { InventoryItemType } from '@kbn/metrics-data-access-plugin/common';
|
|||
export type InfraCustomDashboardAssetType = InventoryItemType;
|
||||
|
||||
export interface InfraCustomDashboard {
|
||||
dashboardIdList: string[];
|
||||
dashboardSavedObjectId: string;
|
||||
assetType: InfraCustomDashboardAssetType;
|
||||
kuery?: string;
|
||||
dashboardFilterAssetIdEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface InfraSavedCustomDashboard extends InfraCustomDashboard {
|
||||
id: string;
|
||||
}
|
||||
|
|
|
@ -12,24 +12,22 @@ const AssetTypeRT = rt.type({
|
|||
assetType: ItemTypeRT,
|
||||
});
|
||||
|
||||
const CustomDashboardRT = rt.intersection([
|
||||
AssetTypeRT,
|
||||
rt.type({
|
||||
dashboardIdList: rt.array(rt.string),
|
||||
}),
|
||||
rt.partial({
|
||||
kuery: rt.string,
|
||||
}),
|
||||
]);
|
||||
const PayloadRT = rt.type({
|
||||
dashboardSavedObjectId: rt.string,
|
||||
dashboardFilterAssetIdEnabled: rt.boolean,
|
||||
});
|
||||
|
||||
const SavedObjectIdRT = rt.type({
|
||||
id: rt.string,
|
||||
});
|
||||
|
||||
const InfraCustomDashboardRT = rt.intersection([AssetTypeRT, PayloadRT, SavedObjectIdRT]);
|
||||
|
||||
/**
|
||||
GET endpoint
|
||||
*/
|
||||
export const InfraGetCustomDashboardsRequestParamsRT = AssetTypeRT;
|
||||
export const InfraGetCustomDashboardsResponseBodyRT = CustomDashboardRT;
|
||||
export type InfraGetCustomDashboardsRequestParams = rt.TypeOf<
|
||||
typeof InfraGetCustomDashboardsRequestParamsRT
|
||||
>;
|
||||
export const InfraGetCustomDashboardsRequestPathParamsRT = AssetTypeRT;
|
||||
export const InfraGetCustomDashboardsResponseBodyRT = rt.array(InfraCustomDashboardRT);
|
||||
export type InfraGetCustomDashboardsResponseBody = rt.TypeOf<
|
||||
typeof InfraGetCustomDashboardsResponseBodyRT
|
||||
>;
|
||||
|
@ -37,11 +35,27 @@ export type InfraGetCustomDashboardsResponseBody = rt.TypeOf<
|
|||
/**
|
||||
* POST endpoint
|
||||
*/
|
||||
export const InfraSaveCustomDashboardsRequestPayloadRT = CustomDashboardRT;
|
||||
export const InfraSaveCustomDashboardsResponseBodyRT = CustomDashboardRT;
|
||||
export const InfraSaveCustomDashboardsRequestPayloadRT = PayloadRT;
|
||||
export const InfraSaveCustomDashboardsResponseBodyRT = InfraCustomDashboardRT;
|
||||
export type InfraSaveCustomDashboardsRequestPayload = rt.TypeOf<
|
||||
typeof InfraSaveCustomDashboardsRequestPayloadRT
|
||||
>;
|
||||
export type InfraSaveCustomDashboardsResponseBody = rt.TypeOf<
|
||||
typeof InfraSaveCustomDashboardsResponseBodyRT
|
||||
>;
|
||||
|
||||
/**
|
||||
* PUT endpoint
|
||||
*/
|
||||
export const InfraUpdateCustomDashboardsRequestPathParamsRT = rt.intersection([
|
||||
AssetTypeRT,
|
||||
SavedObjectIdRT,
|
||||
]);
|
||||
|
||||
/**
|
||||
* DELETE endpoint
|
||||
*/
|
||||
export const InfraDeleteCustomDashboardsRequestParamsRT = rt.intersection([
|
||||
AssetTypeRT,
|
||||
SavedObjectIdRT,
|
||||
]);
|
||||
|
|
|
@ -8,8 +8,12 @@
|
|||
import type { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { initGetCustomDashboardRoute } from './get_custom_dashboard';
|
||||
import { initSaveCustomDashboardRoute } from './save_custom_dashboard';
|
||||
import { initDeleteCustomDashboardRoute } from './delete_custom_dashboard';
|
||||
import { initUpdateCustomDashboardRoute } from './update_custom_dashboard';
|
||||
|
||||
export function initCustomDashboardsRoutes(framework: KibanaFramework) {
|
||||
initGetCustomDashboardRoute(framework);
|
||||
initSaveCustomDashboardRoute(framework);
|
||||
initDeleteCustomDashboardRoute(framework);
|
||||
initUpdateCustomDashboardRoute(framework);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
||||
import { InfraDeleteCustomDashboardsRequestParamsRT } from '../../../common/http_api/custom_dashboards_api';
|
||||
import { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { handleRouteErrors } from '../../utils/handle_route_errors';
|
||||
import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enabled';
|
||||
import { deleteCustomDashboard } from './lib/delete_custom_dashboard';
|
||||
|
||||
export function initDeleteCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validateParams = createRouteValidationFunction(InfraDeleteCustomDashboardsRequestParamsRT);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'delete',
|
||||
path: '/api/infra/{assetType}/custom-dashboards/{id}',
|
||||
validate: {
|
||||
params: validateParams,
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
handleRouteErrors(async (context, request, response) => {
|
||||
const { savedObjectsClient, uiSettingsClient } = await context.infra;
|
||||
|
||||
await checkCustomDashboardsEnabled(uiSettingsClient);
|
||||
|
||||
const { id } = request.params;
|
||||
|
||||
await deleteCustomDashboard({
|
||||
savedObjectsClient,
|
||||
savedObjectId: id,
|
||||
});
|
||||
|
||||
return response.ok({
|
||||
body: id,
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
||||
import {
|
||||
InfraGetCustomDashboardsRequestParamsRT,
|
||||
InfraGetCustomDashboardsRequestPathParamsRT,
|
||||
InfraGetCustomDashboardsResponseBodyRT,
|
||||
} from '../../../common/http_api/custom_dashboards_api';
|
||||
import { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
|
@ -16,12 +16,12 @@ import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enab
|
|||
import { findCustomDashboard } from './lib/find_custom_dashboard';
|
||||
|
||||
export function initGetCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validateParams = createRouteValidationFunction(InfraGetCustomDashboardsRequestParamsRT);
|
||||
const validateParams = createRouteValidationFunction(InfraGetCustomDashboardsRequestPathParamsRT);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'get',
|
||||
path: '/api/infra/custom-dashboards/{assetType}',
|
||||
path: '/api/infra/{assetType}/custom-dashboards',
|
||||
validate: {
|
||||
params: validateParams,
|
||||
},
|
||||
|
@ -37,24 +37,8 @@ export function initGetCustomDashboardRoute(framework: KibanaFramework) {
|
|||
const params = request.params;
|
||||
const customDashboards = await findCustomDashboard(params.assetType, savedObjectsClient);
|
||||
|
||||
if (customDashboards.total === 0) {
|
||||
return response.ok({
|
||||
body: InfraGetCustomDashboardsResponseBodyRT.encode({
|
||||
assetType: params.assetType,
|
||||
dashboardIdList: [],
|
||||
kuery: undefined,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
const attributes = customDashboards.saved_objects[0].attributes;
|
||||
|
||||
return response.ok({
|
||||
body: InfraGetCustomDashboardsResponseBodyRT.encode({
|
||||
assetType: attributes.assetType,
|
||||
dashboardIdList: attributes.dashboardIdList,
|
||||
kuery: attributes.kuery,
|
||||
}),
|
||||
body: InfraGetCustomDashboardsResponseBodyRT.encode(customDashboards),
|
||||
});
|
||||
})
|
||||
);
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { type SavedObjectsClientContract } from '@kbn/core-saved-objects-api-server';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../../saved_objects';
|
||||
|
||||
interface Options {
|
||||
savedObjectsClient: SavedObjectsClientContract;
|
||||
savedObjectId: string;
|
||||
}
|
||||
export function deleteCustomDashboard({ savedObjectsClient, savedObjectId }: Options) {
|
||||
return savedObjectsClient.delete(INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE, savedObjectId);
|
||||
}
|
|
@ -12,13 +12,22 @@ import type {
|
|||
InfraCustomDashboardAssetType,
|
||||
} from '../../../../common/custom_dashboards';
|
||||
|
||||
export function findCustomDashboard(
|
||||
export async function findCustomDashboard(
|
||||
assetType: InfraCustomDashboardAssetType,
|
||||
savedObjectsClient: SavedObjectsClientContract
|
||||
) {
|
||||
return savedObjectsClient.find<InfraCustomDashboard>({
|
||||
const result = await savedObjectsClient.find<InfraCustomDashboard>({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
search: assetType,
|
||||
searchFields: ['assetType'] as [keyof InfraCustomDashboard],
|
||||
page: 1,
|
||||
perPage: 1000,
|
||||
sortField: 'updated_at',
|
||||
sortOrder: 'desc',
|
||||
});
|
||||
|
||||
return result.saved_objects.map(({ id, attributes }) => ({
|
||||
id,
|
||||
...attributes,
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -9,24 +9,26 @@ import { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
|||
import { InfraCustomDashboard } from '../../../common/custom_dashboards';
|
||||
import {
|
||||
InfraSaveCustomDashboardsRequestPayloadRT,
|
||||
InfraSaveCustomDashboardsRequestPayload,
|
||||
InfraSaveCustomDashboardsResponseBodyRT,
|
||||
InfraGetCustomDashboardsRequestPathParamsRT,
|
||||
} from '../../../common/http_api/custom_dashboards_api';
|
||||
import { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../saved_objects';
|
||||
import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enabled';
|
||||
import { findCustomDashboard } from './lib/find_custom_dashboard';
|
||||
import { handleRouteErrors } from '../../utils/handle_route_errors';
|
||||
import { findCustomDashboard } from './lib/find_custom_dashboard';
|
||||
|
||||
export function initSaveCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validatePayload = createRouteValidationFunction(InfraSaveCustomDashboardsRequestPayloadRT);
|
||||
const validateParams = createRouteValidationFunction(InfraGetCustomDashboardsRequestPathParamsRT);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'post',
|
||||
path: '/api/infra/custom-dashboards',
|
||||
path: '/api/infra/{assetType}/custom-dashboards',
|
||||
validate: {
|
||||
body: validatePayload,
|
||||
params: validateParams,
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
|
@ -37,29 +39,33 @@ export function initSaveCustomDashboardRoute(framework: KibanaFramework) {
|
|||
|
||||
await checkCustomDashboardsEnabled(uiSettingsClient);
|
||||
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = request.body;
|
||||
const customDashboards = await findCustomDashboard(payload.assetType, savedObjectsClient);
|
||||
const { dashboardSavedObjectId } = request.body;
|
||||
|
||||
if (customDashboards.total === 0) {
|
||||
const savedCustomDashboard = await savedObjectsClient.create<InfraCustomDashboard>(
|
||||
INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
payload
|
||||
);
|
||||
const { assetType } = request.params;
|
||||
|
||||
return response.ok({
|
||||
body: InfraSaveCustomDashboardsResponseBodyRT.encode(savedCustomDashboard.attributes),
|
||||
const customDashboards = await findCustomDashboard(assetType, savedObjectsClient);
|
||||
|
||||
const dashboardExist = customDashboards.find(
|
||||
(customDashboard) => customDashboard.dashboardSavedObjectId === dashboardSavedObjectId
|
||||
);
|
||||
|
||||
if (dashboardExist) {
|
||||
return response.badRequest({
|
||||
body: `Dashboard with id ${dashboardSavedObjectId} has already been linked to ${assetType}`,
|
||||
});
|
||||
}
|
||||
|
||||
const savedCustomDashboard = await savedObjectsClient.update<InfraCustomDashboard>(
|
||||
const savedCustomDashboard = await savedObjectsClient.create<InfraCustomDashboard>(
|
||||
INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
customDashboards.saved_objects[0].id,
|
||||
payload
|
||||
{
|
||||
assetType,
|
||||
...request.body,
|
||||
}
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: InfraSaveCustomDashboardsResponseBodyRT.encode({
|
||||
...payload,
|
||||
id: savedCustomDashboard.id,
|
||||
...savedCustomDashboard.attributes,
|
||||
}),
|
||||
});
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* 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 { createRouteValidationFunction } from '@kbn/io-ts-utils';
|
||||
import { InfraCustomDashboard } from '../../../common/custom_dashboards';
|
||||
import {
|
||||
InfraSaveCustomDashboardsRequestPayloadRT,
|
||||
InfraSaveCustomDashboardsResponseBodyRT,
|
||||
InfraUpdateCustomDashboardsRequestPathParamsRT,
|
||||
} from '../../../common/http_api/custom_dashboards_api';
|
||||
import { KibanaFramework } from '../../lib/adapters/framework/kibana_framework_adapter';
|
||||
import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '../../saved_objects';
|
||||
import { checkCustomDashboardsEnabled } from './lib/check_custom_dashboards_enabled';
|
||||
import { handleRouteErrors } from '../../utils/handle_route_errors';
|
||||
|
||||
export function initUpdateCustomDashboardRoute(framework: KibanaFramework) {
|
||||
const validatePayload = createRouteValidationFunction(InfraSaveCustomDashboardsRequestPayloadRT);
|
||||
const validateParams = createRouteValidationFunction(
|
||||
InfraUpdateCustomDashboardsRequestPathParamsRT
|
||||
);
|
||||
|
||||
framework.registerRoute(
|
||||
{
|
||||
method: 'put',
|
||||
path: '/api/infra/{assetType}/custom-dashboards/{id}',
|
||||
validate: {
|
||||
body: validatePayload,
|
||||
params: validateParams,
|
||||
},
|
||||
options: {
|
||||
access: 'internal',
|
||||
},
|
||||
},
|
||||
handleRouteErrors(async (context, request, response) => {
|
||||
const { savedObjectsClient, uiSettingsClient } = await context.infra;
|
||||
|
||||
await checkCustomDashboardsEnabled(uiSettingsClient);
|
||||
|
||||
const { id, assetType } = request.params;
|
||||
|
||||
const savedCustomDashboard = await savedObjectsClient.update<InfraCustomDashboard>(
|
||||
INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
id,
|
||||
{
|
||||
assetType,
|
||||
...request.body,
|
||||
}
|
||||
);
|
||||
|
||||
return response.ok({
|
||||
body: InfraSaveCustomDashboardsResponseBodyRT.encode({
|
||||
id: savedCustomDashboard.id,
|
||||
assetType,
|
||||
...request.body,
|
||||
...savedCustomDashboard.attributes,
|
||||
}),
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
|
@ -7,20 +7,21 @@
|
|||
|
||||
import { SavedObjectsFieldMapping, SavedObjectsType } from '@kbn/core/server';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { schema, Type } from '@kbn/config-schema';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { InfraCustomDashboard } from '../../../common/custom_dashboards';
|
||||
|
||||
export const INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE = 'infra-custom-dashboards';
|
||||
|
||||
const properties: Record<keyof InfraCustomDashboard, SavedObjectsFieldMapping> = {
|
||||
dashboardIdList: { type: 'keyword' },
|
||||
assetType: { type: 'keyword' },
|
||||
kuery: { type: 'text' },
|
||||
};
|
||||
const createSchema: Record<keyof InfraCustomDashboard, Type<any>> = {
|
||||
dashboardIdList: schema.arrayOf(schema.string()),
|
||||
assetType: schema.string(),
|
||||
kuery: schema.maybe(schema.string()),
|
||||
assetType: {
|
||||
type: 'keyword',
|
||||
},
|
||||
dashboardSavedObjectId: {
|
||||
type: 'keyword',
|
||||
},
|
||||
dashboardFilterAssetIdEnabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
};
|
||||
|
||||
export const infraCustomDashboardsSavedObjectType: SavedObjectsType = {
|
||||
|
@ -28,6 +29,7 @@ export const infraCustomDashboardsSavedObjectType: SavedObjectsType = {
|
|||
hidden: false,
|
||||
namespaceType: 'multiple',
|
||||
mappings: {
|
||||
dynamic: false,
|
||||
properties,
|
||||
},
|
||||
management: {
|
||||
|
@ -42,7 +44,37 @@ export const infraCustomDashboardsSavedObjectType: SavedObjectsType = {
|
|||
'1': {
|
||||
changes: [],
|
||||
schemas: {
|
||||
create: schema.object(createSchema),
|
||||
create: schema.object({
|
||||
dashboardIdList: schema.arrayOf(schema.string()),
|
||||
assetType: schema.string(),
|
||||
kuery: schema.maybe(schema.string()),
|
||||
}),
|
||||
},
|
||||
},
|
||||
'2': {
|
||||
changes: [
|
||||
{
|
||||
type: 'mappings_addition',
|
||||
addedMappings: {
|
||||
dashboardSavedObjectId: {
|
||||
type: 'keyword',
|
||||
},
|
||||
dashboardFilterAssetIdEnabled: {
|
||||
type: 'boolean',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'mappings_deprecation',
|
||||
deprecatedMappings: ['dashboardIdList', 'kuery'],
|
||||
},
|
||||
],
|
||||
schemas: {
|
||||
create: schema.object({
|
||||
dashboardSavedObjectId: schema.string(),
|
||||
assetType: schema.string(),
|
||||
dashboardFilterAssetIdEnabled: schema.boolean(),
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -12,7 +12,10 @@ import { INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE } from '@kbn/infra-plugin/ser
|
|||
import { enableInfrastructureAssetCustomDashboards } from '@kbn/observability-plugin/common';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
const CUSTOM_DASHBOARDS_API_URL = '/api/infra/custom-dashboards';
|
||||
const getCustomDashboardsUrl = (assetType: string, dashboardSavedObjectId?: string) =>
|
||||
dashboardSavedObjectId
|
||||
? `/api/infra/${assetType}/custom-dashboards/${dashboardSavedObjectId}`
|
||||
: `/api/infra/${assetType}/custom-dashboards`;
|
||||
|
||||
export default function ({ getService }: FtrProviderContext) {
|
||||
const supertest = getService('supertest');
|
||||
|
@ -31,7 +34,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertest.get(`${CUSTOM_DASHBOARDS_API_URL}/host`).expect(403);
|
||||
await supertest.get(getCustomDashboardsUrl('host')).expect(403);
|
||||
});
|
||||
|
||||
it('responds with an error when trying to request a custom dashboard for unsupported asset type', async () => {
|
||||
|
@ -39,7 +42,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertest.get(`${CUSTOM_DASHBOARDS_API_URL}/unsupported-asset-type`).expect(400);
|
||||
await supertest.get(getCustomDashboardsUrl('unsupported-asset-type')).expect(400);
|
||||
});
|
||||
|
||||
it('responds with an empty configuration if custom dashboard saved object does not exist', async () => {
|
||||
|
@ -50,18 +53,16 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
|
||||
const response = await supertest.get(`${CUSTOM_DASHBOARDS_API_URL}/host`).expect(200);
|
||||
const response = await supertest.get(getCustomDashboardsUrl('host')).expect(200);
|
||||
|
||||
expect(response.body).to.be.eql({
|
||||
assetType: 'host',
|
||||
dashboardIdList: [],
|
||||
});
|
||||
expect(response.body).to.be.eql([]);
|
||||
});
|
||||
|
||||
it('responds with the custom dashboard configuration for a given asset type when it exists', async () => {
|
||||
const customDashboard: InfraCustomDashboard = {
|
||||
assetType: 'host',
|
||||
dashboardIdList: ['123'],
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
|
@ -72,24 +73,27 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
overwrite: true,
|
||||
});
|
||||
|
||||
const response = await supertest.get(`${CUSTOM_DASHBOARDS_API_URL}/host`).expect(200);
|
||||
const response = await supertest.get(getCustomDashboardsUrl('host')).expect(200);
|
||||
|
||||
expect(response.body).to.be.eql(customDashboard);
|
||||
expect(response.body).to.have.length(1);
|
||||
expect(response.body[0]).to.have.property('dashboardFilterAssetIdEnabled', true);
|
||||
expect(response.body[0]).to.have.property('assetType', 'host');
|
||||
expect(response.body[0]).to.have.property('dashboardSavedObjectId', '123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST endpoint for saving (creating or updating) custom dashboard', () => {
|
||||
describe('POST endpoint for saving custom dashboard', () => {
|
||||
it('responds with an error if Custom Dashboards UI setting is not enabled', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
assetType: 'host',
|
||||
dashboardIdList: ['123'],
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertest
|
||||
.post(`${CUSTOM_DASHBOARDS_API_URL}`)
|
||||
.post(getCustomDashboardsUrl('host'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(payload)
|
||||
.expect(403);
|
||||
|
@ -97,15 +101,15 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
it('responds with an error when trying to update a custom dashboard for unsupported asset type', async () => {
|
||||
const payload = {
|
||||
assetType: 'unsupported-asset-type',
|
||||
dashboardIdList: ['123'],
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
|
||||
await supertest
|
||||
.post(`${CUSTOM_DASHBOARDS_API_URL}`)
|
||||
.post(getCustomDashboardsUrl('unsupported-asset-type'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(payload)
|
||||
.expect(400);
|
||||
|
@ -113,8 +117,8 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
it('creates a new dashboard configuration when saving for the first time', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
assetType: 'host',
|
||||
dashboardIdList: ['123'],
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
|
@ -124,15 +128,23 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
});
|
||||
|
||||
const response = await supertest
|
||||
.post(`${CUSTOM_DASHBOARDS_API_URL}`)
|
||||
.post(getCustomDashboardsUrl('host'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(payload)
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).to.be.eql(payload);
|
||||
expect(response.body).to.have.property('id');
|
||||
expect(response.body).to.have.property('dashboardFilterAssetIdEnabled', true);
|
||||
expect(response.body).to.have.property('assetType', 'host');
|
||||
expect(response.body).to.have.property('dashboardSavedObjectId', '123');
|
||||
});
|
||||
|
||||
it('updates existing dashboard configuration when for a given asset type', async () => {
|
||||
it('returns 400 when the dashboard already exist and tries to create it again', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
|
@ -143,24 +155,160 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardIdList: ['123'],
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
const response = await supertest
|
||||
.post(getCustomDashboardsUrl('host'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(payload)
|
||||
.expect(400);
|
||||
|
||||
expect(response.body.error).to.be.eql('Bad Request');
|
||||
expect(response.body.message).to.be.eql(
|
||||
'Dashboard with id 123 has already been linked to host'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PUT endpoint for updating custom dashboard', () => {
|
||||
it('responds with an error if Custom Dashboards UI setting is not enabled', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertest
|
||||
.put(getCustomDashboardsUrl('host', '123'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(payload)
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('responds with an error when trying to update not existing dashboard', async () => {
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
};
|
||||
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
|
||||
await supertest
|
||||
.put(getCustomDashboardsUrl('host', '000'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(payload)
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('updates existing dashboard configuration for a given asset type', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '456',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
const existingDashboardSavedObject = await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
const payload: InfraSaveCustomDashboardsRequestPayload = {
|
||||
assetType: 'host',
|
||||
dashboardIdList: ['123', '456'],
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: false,
|
||||
};
|
||||
const updateResponse = await supertest
|
||||
.post(`${CUSTOM_DASHBOARDS_API_URL}`)
|
||||
.put(getCustomDashboardsUrl('host', existingDashboardSavedObject.id))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.send(payload)
|
||||
.expect(200);
|
||||
const getResponse = await supertest.get(`${CUSTOM_DASHBOARDS_API_URL}/host`).expect(200);
|
||||
const getResponse = await supertest.get(getCustomDashboardsUrl('host')).expect(200);
|
||||
|
||||
expect(updateResponse.body).to.be.eql(payload);
|
||||
expect(getResponse.body).to.be.eql(payload);
|
||||
expect(updateResponse.body).to.be.eql({
|
||||
...payload,
|
||||
assetType: 'host',
|
||||
id: updateResponse.body.id,
|
||||
});
|
||||
|
||||
expect(getResponse.body).to.have.length(2);
|
||||
expect(getResponse.body[0]).to.have.property('dashboardSavedObjectId', '123');
|
||||
expect(getResponse.body[0]).to.have.property('dashboardFilterAssetIdEnabled', false);
|
||||
expect(getResponse.body[0]).to.have.property('assetType', 'host');
|
||||
expect(getResponse.body[1]).to.have.property('dashboardSavedObjectId', '456');
|
||||
expect(getResponse.body[1]).to.have.property('dashboardFilterAssetIdEnabled', true);
|
||||
expect(getResponse.body[1]).to.have.property('assetType', 'host');
|
||||
});
|
||||
});
|
||||
|
||||
describe('DELETE endpoint for removing a custom dashboard', () => {
|
||||
it('responds with an error if Custom Dashboards UI setting is not enabled', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: false,
|
||||
});
|
||||
|
||||
await supertest
|
||||
.delete(getCustomDashboardsUrl('host', '123'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(403);
|
||||
});
|
||||
|
||||
it('responds with an error when trying to delete not existing dashboard', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
|
||||
await supertest
|
||||
.delete(getCustomDashboardsUrl('host', '000'))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(404);
|
||||
});
|
||||
|
||||
it('deletes an existing dashboard', async () => {
|
||||
await kibanaServer.uiSettings.update({
|
||||
[enableInfrastructureAssetCustomDashboards]: true,
|
||||
});
|
||||
await kibanaServer.savedObjects.clean({
|
||||
types: [INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE],
|
||||
});
|
||||
const existingDashboardSavedObject = await kibanaServer.savedObjects.create({
|
||||
type: INFRA_CUSTOM_DASHBOARDS_SAVED_OBJECT_TYPE,
|
||||
attributes: {
|
||||
assetType: 'host',
|
||||
dashboardSavedObjectId: '123',
|
||||
dashboardFilterAssetIdEnabled: true,
|
||||
},
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
await supertest
|
||||
.delete(getCustomDashboardsUrl('host', existingDashboardSavedObject.id))
|
||||
.set('kbn-xsrf', 'xxx')
|
||||
.expect(200);
|
||||
|
||||
const afterDeleteResponse = await supertest.get(getCustomDashboardsUrl('host')).expect(200);
|
||||
|
||||
expect(afterDeleteResponse.body).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue