Skip to content

Commit

Permalink
[Infrastructure UI] Implement Metrics explorer views CRUD endpoints (#…
Browse files Browse the repository at this point in the history
…155621)

## 📓  Summary

Part of #152617 
Closes #155111  

This PR implements the CRUD endpoints for the metrics explorer views.
Following the approach used for the InventoryView service, it exposes a
client that abstracts all the logic concerned to the
`metrics-explorer-view` saved objects.

It also follows the guideline provided for [Versioning
interfaces](https://docs.elastic.dev/kibana-dev-docs/versioning-interfaces)
and [Versioning HTTP
APIs](https://docs.elastic.dev/kibana-dev-docs/versioning-http-apis),
preparing for the serverless.

## 🤓 Tips for the reviewer
You can open the Kibana dev tools and play with the following snippet to
test the create APIs, or you can perform the same requests with your
preferred client:
```
// Get all
GET kbn:/api/infra/metrics_explorer_views

// Create one
POST kbn:/api/infra/metrics_explorer_views
{
  "attributes": {
    "name": "My view"
  }
}

// Get one
GET kbn:/api/infra/metrics_explorer_views/<switch-with-id>

// Update one
PUT kbn:/api/infra/metrics_explorer_views/<switch-with-id>
{
  "attributes": {
    "name": "My view 2"
  }
}

// Delete one
DELETE kbn:/api/infra/metrics_explorer_views/<switch-with-id>
```

---------

Co-authored-by: Marco Antonio Ghiani <[email protected]>
Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
3 people authored Apr 26, 2023
1 parent 202f13f commit 61bb52c
Show file tree
Hide file tree
Showing 38 changed files with 1,528 additions and 23 deletions.
1 change: 1 addition & 0 deletions x-pack/plugins/infra/common/http_api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ export * from './infra';
*/
export * from './latest';
export * as inventoryViewsV1 from './inventory_views/v1';
export * as metricsExplorerViewsV1 from './metrics_explorer_views/v1';
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ export const inventoryViewResponsePayloadRT = rt.type({
data: inventoryViewResponseRT,
});

export type GetInventoryViewResponsePayload = rt.TypeOf<typeof inventoryViewResponsePayloadRT>;
export type InventoryViewResponsePayload = rt.TypeOf<typeof inventoryViewResponsePayloadRT>;
1 change: 1 addition & 0 deletions x-pack/plugins/infra/common/http_api/latest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
*/

export * from './inventory_views/v1';
export * from './metrics_explorer_views/v1';
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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 { nonEmptyStringRt } from '@kbn/io-ts-utils';
import * as rt from 'io-ts';
import { either } from 'fp-ts/Either';

export const METRICS_EXPLORER_VIEW_URL = '/api/infra/metrics_explorer_views';
export const METRICS_EXPLORER_VIEW_URL_ENTITY = `${METRICS_EXPLORER_VIEW_URL}/{metricsExplorerViewId}`;
export const getMetricsExplorerViewUrl = (metricsExplorerViewId?: string) =>
[METRICS_EXPLORER_VIEW_URL, metricsExplorerViewId].filter(Boolean).join('/');

const metricsExplorerViewIdRT = new rt.Type<string, string, unknown>(
'MetricsExplorerViewId',
rt.string.is,
(u, c) =>
either.chain(rt.string.validate(u, c), (id) => {
return id === '0'
? rt.failure(u, c, `The metrics explorer view with id ${id} is not configurable.`)
: rt.success(id);
}),
String
);

export const metricsExplorerViewRequestParamsRT = rt.type({
metricsExplorerViewId: metricsExplorerViewIdRT,
});

export type MetricsExplorerViewRequestParams = rt.TypeOf<typeof metricsExplorerViewRequestParamsRT>;

export const metricsExplorerViewRequestQueryRT = rt.partial({
sourceId: rt.string,
});

export type MetricsExplorerViewRequestQuery = rt.TypeOf<typeof metricsExplorerViewRequestQueryRT>;

const metricsExplorerViewAttributesResponseRT = rt.intersection([
rt.strict({
name: nonEmptyStringRt,
isDefault: rt.boolean,
isStatic: rt.boolean,
}),
rt.UnknownRecord,
]);

const metricsExplorerViewResponseRT = rt.exact(
rt.intersection([
rt.type({
id: rt.string,
attributes: metricsExplorerViewAttributesResponseRT,
}),
rt.partial({
updatedAt: rt.number,
version: rt.string,
}),
])
);

export const metricsExplorerViewResponsePayloadRT = rt.type({
data: metricsExplorerViewResponseRT,
});

export type GetMetricsExplorerViewResponsePayload = rt.TypeOf<
typeof metricsExplorerViewResponsePayloadRT
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { nonEmptyStringRt } from '@kbn/io-ts-utils';
import * as rt from 'io-ts';

export const createMetricsExplorerViewAttributesRequestPayloadRT = rt.intersection([
rt.type({
name: nonEmptyStringRt,
}),
rt.UnknownRecord,
rt.exact(rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined })),
]);

export type CreateMetricsExplorerViewAttributesRequestPayload = rt.TypeOf<
typeof createMetricsExplorerViewAttributesRequestPayloadRT
>;

export const createMetricsExplorerViewRequestPayloadRT = rt.type({
attributes: createMetricsExplorerViewAttributesRequestPayloadRT,
});

export type CreateMetricsExplorerViewRequestPayload = rt.TypeOf<
typeof createMetricsExplorerViewRequestPayloadRT
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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 { nonEmptyStringRt } from '@kbn/io-ts-utils';
import * as rt from 'io-ts';

export const findMetricsExplorerViewAttributesResponseRT = rt.strict({
name: nonEmptyStringRt,
isDefault: rt.boolean,
isStatic: rt.boolean,
});

const findMetricsExplorerViewResponseRT = rt.exact(
rt.intersection([
rt.type({
id: rt.string,
attributes: findMetricsExplorerViewAttributesResponseRT,
}),
rt.partial({
updatedAt: rt.number,
version: rt.string,
}),
])
);

export const findMetricsExplorerViewResponsePayloadRT = rt.type({
data: rt.array(findMetricsExplorerViewResponseRT),
});

export type FindMetricsExplorerViewResponsePayload = rt.TypeOf<
typeof findMetricsExplorerViewResponsePayloadRT
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* 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 * as rt from 'io-ts';

export const getMetricsExplorerViewRequestParamsRT = rt.type({
metricsExplorerViewId: rt.string,
});

export type GetMetricsExplorerViewRequestParams = rt.TypeOf<
typeof getMetricsExplorerViewRequestParamsRT
>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* 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.
*/

export * from './common';
export * from './get_metrics_explorer_view';
export * from './find_metrics_explorer_view';
export * from './create_metrics_explorer_view';
export * from './update_metrics_explorer_view';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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 { nonEmptyStringRt } from '@kbn/io-ts-utils';
import * as rt from 'io-ts';

export const updateMetricsExplorerViewAttributesRequestPayloadRT = rt.intersection([
rt.type({
name: nonEmptyStringRt,
}),
rt.UnknownRecord,
rt.exact(rt.partial({ isDefault: rt.undefined, isStatic: rt.undefined })),
]);

export type UpdateMetricsExplorerViewAttributesRequestPayload = rt.TypeOf<
typeof updateMetricsExplorerViewAttributesRequestPayloadRT
>;

export const updateMetricsExplorerViewRequestPayloadRT = rt.type({
attributes: updateMetricsExplorerViewAttributesRequestPayloadRT,
});

export type UpdateMetricsExplorerViewRequestPayload = rt.TypeOf<
typeof updateMetricsExplorerViewRequestPayloadRT
>;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { i18n } from '@kbn/i18n';
import type { NonEmptyString } from '@kbn/io-ts-utils';
import type { MetricsExplorerViewAttributes } from './types';

export const staticMetricsExplorerViewId = 'static';
export const staticMetricsExplorerViewId = '0';

export const staticMetricsExplorerViewAttributes: MetricsExplorerViewAttributes = {
name: i18n.translate('xpack.infra.savedView.defaultViewNameHosts', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
* 2.0.
*/

export * from './defaults';
export * from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import { staticMetricsExplorerViewAttributes } from './defaults';
import type { MetricsExplorerView, MetricsExplorerViewAttributes } from './types';

export const createmetricsExplorerViewMock = (
export const createMetricsExplorerViewMock = (
id: string,
attributes: MetricsExplorerViewAttributes,
updatedAt?: number,
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/infra/server/infra_server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { initOverviewRoute } from './routes/overview';
import { initProcessListRoute } from './routes/process_list';
import { initSnapshotRoute } from './routes/snapshot';
import { initInfraMetricsRoute } from './routes/infra';
import { initMetricsExplorerViewRoutes } from './routes/metrics_explorer_views';

export const initInfraServer = (libs: InfraBackendLibs) => {
initIpToHostName(libs);
Expand All @@ -59,6 +60,7 @@ export const initInfraServer = (libs: InfraBackendLibs) => {
initLogEntriesSummaryHighlightsRoute(libs);
initLogViewRoutes(libs);
initMetricExplorerRoute(libs);
initMetricsExplorerViewRoutes(libs);
initMetricsAPIRoute(libs);
initMetadataRoute(libs);
initInventoryMetaRoute(libs);
Expand Down
2 changes: 2 additions & 0 deletions x-pack/plugins/infra/server/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
createLogViewsServiceSetupMock,
createLogViewsServiceStartMock,
} from './services/log_views/log_views_service.mock';
import { createMetricsExplorerViewsServiceStartMock } from './services/metrics_explorer_views/metrics_explorer_views_service.mock';
import { InfraPluginSetup, InfraPluginStart } from './types';

const createInfraSetupMock = () => {
Expand All @@ -26,6 +27,7 @@ const createInfraStartMock = () => {
getMetricIndices: jest.fn(),
inventoryViews: createInventoryViewsServiceStartMock(),
logViews: createLogViewsServiceStartMock(),
metricsExplorerViews: createMetricsExplorerViewsServiceStartMock(),
};
return infraStartMock;
};
Expand Down
15 changes: 14 additions & 1 deletion x-pack/plugins/infra/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import {
import { InventoryViewsService } from './services/inventory_views';
import { LogEntriesService } from './services/log_entries';
import { LogViewsService } from './services/log_views';
import { MetricsExplorerViewsService } from './services/metrics_explorer_views';
import { RulesService } from './services/rules';
import {
InfraConfig,
Expand Down Expand Up @@ -122,6 +123,7 @@ export class InfraServerPlugin
private metricsRules: RulesService;
private inventoryViews: InventoryViewsService;
private logViews: LogViewsService;
private metricsExplorerViews: MetricsExplorerViewsService;

constructor(context: PluginInitializerContext<InfraConfig>) {
this.config = context.config.get();
Expand All @@ -140,6 +142,9 @@ export class InfraServerPlugin

this.inventoryViews = new InventoryViewsService(this.logger.get('inventoryViews'));
this.logViews = new LogViewsService(this.logger.get('logViews'));
this.metricsExplorerViews = new MetricsExplorerViewsService(
this.logger.get('metricsExplorerViews')
);
}

setup(core: InfraPluginCoreSetup, plugins: InfraServerPluginSetupDeps) {
Expand All @@ -155,12 +160,13 @@ export class InfraServerPlugin
);
const inventoryViews = this.inventoryViews.setup();
const logViews = this.logViews.setup();
const metricsExplorerViews = this.metricsExplorerViews.setup();

// register saved object types
core.savedObjects.registerType(infraSourceConfigurationSavedObjectType);
core.savedObjects.registerType(metricsExplorerViewSavedObjectType);
core.savedObjects.registerType(inventoryViewSavedObjectType);
core.savedObjects.registerType(logViewSavedObjectType);
core.savedObjects.registerType(metricsExplorerViewSavedObjectType);

// TODO: separate these out individually and do away with "domains" as a temporary group
// and make them available via the request context so we can do away with
Expand Down Expand Up @@ -237,6 +243,7 @@ export class InfraServerPlugin
defineInternalSourceConfiguration: sources.defineInternalSourceConfiguration.bind(sources),
inventoryViews,
logViews,
metricsExplorerViews,
} as InfraPluginSetup;
}

Expand All @@ -258,9 +265,15 @@ export class InfraServerPlugin
},
});

const metricsExplorerViews = this.metricsExplorerViews.start({
infraSources: this.libs.sources,
savedObjects: core.savedObjects,
});

return {
inventoryViews,
logViews,
metricsExplorerViews,
getMetricIndices: makeGetMetricIndices(this.libs.sources),
};
}
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/infra/server/routes/inventory_views/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ Updates an inventory view.

Any attribute can be updated except for `isDefault` and `isStatic`, which are derived by the source configuration preference set by the user.

Any attempt to update the static view with id `0` will return a `400 The inventory view with id 0 is not configurable.`

### Request

- **Method**: PUT
Expand Down Expand Up @@ -324,6 +326,8 @@ Status code: 409

Deletes an inventory view.

Any attempt to delete the static view with id `0` will return a `400 The inventory view with id 0 is not configurable.`

### Request

- **Method**: DELETE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const initCreateInventoryViewRoute = ({
},
async (_requestContext, request, response) => {
const { body } = request;
const { inventoryViews } = (await getStartServices())[2];
const [, , { inventoryViews }] = await getStartServices();
const inventoryViewsClient = inventoryViews.getScopedClient(request);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const initDeleteInventoryViewRoute = ({
},
async (_requestContext, request, response) => {
const { params } = request;
const { inventoryViews } = (await getStartServices())[2];
const [, , { inventoryViews }] = await getStartServices();
const inventoryViewsClient = inventoryViews.getScopedClient(request);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const initFindInventoryViewRoute = ({
},
async (_requestContext, request, response) => {
const { query } = request;
const { inventoryViews } = (await getStartServices())[2];
const [, , { inventoryViews }] = await getStartServices();
const inventoryViewsClient = inventoryViews.getScopedClient(request);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export const initGetInventoryViewRoute = ({
},
async (_requestContext, request, response) => {
const { params, query } = request;
const { inventoryViews } = (await getStartServices())[2];
const [, , { inventoryViews }] = await getStartServices();
const inventoryViewsClient = inventoryViews.getScopedClient(request);

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const initUpdateInventoryViewRoute = ({
},
async (_requestContext, request, response) => {
const { body, params, query } = request;
const { inventoryViews } = (await getStartServices())[2];
const [, , { inventoryViews }] = await getStartServices();
const inventoryViewsClient = inventoryViews.getScopedClient(request);

try {
Expand Down
Loading

0 comments on commit 61bb52c

Please sign in to comment.