diff --git a/x-pack/plugins/infra/common/metrics_explorer_views/errors.ts b/x-pack/plugins/infra/common/metrics_explorer_views/errors.ts new file mode 100644 index 0000000000000..3d2ee6aeb8da6 --- /dev/null +++ b/x-pack/plugins/infra/common/metrics_explorer_views/errors.ts @@ -0,0 +1,32 @@ +/* + * 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. + */ + +/* eslint-disable max-classes-per-file */ + +export class FetchMetricsExplorerViewError extends Error { + constructor(message: string, public cause?: Error) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + this.name = 'FetchMetricsExplorerViewError'; + } +} + +export class UpsertMetricsExplorerViewError extends Error { + constructor(message: string, public cause?: Error) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + this.name = 'UpsertMetricsExplorerViewError'; + } +} + +export class DeleteMetricsExplorerViewError extends Error { + constructor(message: string, public cause?: Error) { + super(message); + Object.setPrototypeOf(this, new.target.prototype); + this.name = 'DeleteMetricsExplorerViewError'; + } +} diff --git a/x-pack/plugins/infra/common/metrics_explorer_views/index.ts b/x-pack/plugins/infra/common/metrics_explorer_views/index.ts index ae809a6c7c615..fa2f6ff7ed001 100644 --- a/x-pack/plugins/infra/common/metrics_explorer_views/index.ts +++ b/x-pack/plugins/infra/common/metrics_explorer_views/index.ts @@ -6,4 +6,5 @@ */ export * from './defaults'; +export * from './errors'; export * from './types'; diff --git a/x-pack/plugins/infra/public/mocks.tsx b/x-pack/plugins/infra/public/mocks.tsx index 40209529fa837..cd94e33b99f5e 100644 --- a/x-pack/plugins/infra/public/mocks.tsx +++ b/x-pack/plugins/infra/public/mocks.tsx @@ -8,12 +8,14 @@ import React from 'react'; import { createInventoryViewsServiceStartMock } from './services/inventory_views/inventory_views_service.mock'; import { createLogViewsServiceStartMock } from './services/log_views/log_views_service.mock'; +import { createMetricsExplorerViewsServiceStartMock } from './services/metrics_explorer_views/metrics_explorer_views_service.mock'; import { createTelemetryServiceMock } from './services/telemetry/telemetry_service.mock'; import { InfraClientStartExports } from './types'; export const createInfraPluginStartMock = () => ({ inventoryViews: createInventoryViewsServiceStartMock(), logViews: createLogViewsServiceStartMock(), + metricsExplorerViews: createMetricsExplorerViewsServiceStartMock(), telemetry: createTelemetryServiceMock(), ContainerMetricsTable: () =>
, HostMetricsTable: () =>
, diff --git a/x-pack/plugins/infra/public/plugin.ts b/x-pack/plugins/infra/public/plugin.ts index 5aecffde99da9..37f55b8b98fc7 100644 --- a/x-pack/plugins/infra/public/plugin.ts +++ b/x-pack/plugins/infra/public/plugin.ts @@ -31,6 +31,7 @@ import { createMetricsFetchData, createMetricsHasData } from './metrics_overview import { registerFeatures } from './register_feature'; import { InventoryViewsService } from './services/inventory_views'; import { LogViewsService } from './services/log_views'; +import { MetricsExplorerViewsService } from './services/metrics_explorer_views'; import { TelemetryService } from './services/telemetry'; import { InfraClientCoreSetup, @@ -47,6 +48,7 @@ export class Plugin implements InfraClientPluginClass { public config: InfraPublicConfig; private inventoryViews: InventoryViewsService; private logViews: LogViewsService; + private metricsExplorerViews: MetricsExplorerViewsService; private telemetry: TelemetryService; private readonly appUpdater$ = new BehaviorSubject(() => ({})); @@ -57,6 +59,7 @@ export class Plugin implements InfraClientPluginClass { messageFields: this.config.sources?.default?.fields?.message ?? defaultLogViewsStaticConfig.messageFields, }); + this.metricsExplorerViews = new MetricsExplorerViewsService(); this.telemetry = new TelemetryService(); } @@ -298,11 +301,16 @@ export class Plugin implements InfraClientPluginClass { search: plugins.data.search, }); + const metricsExplorerViews = this.metricsExplorerViews.start({ + http: core.http, + }); + const telemetry = this.telemetry.start(); const startContract: InfraClientStartExports = { inventoryViews, logViews, + metricsExplorerViews, telemetry, ContainerMetricsTable: createLazyContainerMetricsTable(getStartServices), HostMetricsTable: createLazyHostMetricsTable(getStartServices), diff --git a/x-pack/plugins/infra/public/services/metrics_explorer_views/index.ts b/x-pack/plugins/infra/public/services/metrics_explorer_views/index.ts new file mode 100644 index 0000000000000..be526e0ce8b63 --- /dev/null +++ b/x-pack/plugins/infra/public/services/metrics_explorer_views/index.ts @@ -0,0 +1,10 @@ +/* + * 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 './metrics_explorer_views_client'; +export * from './metrics_explorer_views_service'; +export * from './types'; diff --git a/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.mock.ts b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.mock.ts new file mode 100644 index 0000000000000..a612a22fe5758 --- /dev/null +++ b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.mock.ts @@ -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 { IMetricsExplorerViewsClient } from './types'; + +export const createMetricsExplorerViewsClientMock = + (): jest.Mocked => ({ + findMetricsExplorerViews: jest.fn(), + getMetricsExplorerView: jest.fn(), + createMetricsExplorerView: jest.fn(), + updateMetricsExplorerView: jest.fn(), + deleteMetricsExplorerView: jest.fn(), + }); diff --git a/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.ts b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.ts new file mode 100644 index 0000000000000..d37d2fc81b31d --- /dev/null +++ b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_client.ts @@ -0,0 +1,130 @@ +/* + * 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 { HttpStart } from '@kbn/core/public'; +import { + CreateMetricsExplorerViewAttributesRequestPayload, + createMetricsExplorerViewRequestPayloadRT, + findMetricsExplorerViewResponsePayloadRT, + getMetricsExplorerViewUrl, + metricsExplorerViewResponsePayloadRT, + UpdateMetricsExplorerViewAttributesRequestPayload, +} from '../../../common/http_api/latest'; +import { + DeleteMetricsExplorerViewError, + FetchMetricsExplorerViewError, + MetricsExplorerView, + UpsertMetricsExplorerViewError, +} from '../../../common/metrics_explorer_views'; +import { decodeOrThrow } from '../../../common/runtime_types'; +import { IMetricsExplorerViewsClient } from './types'; + +export class MetricsExplorerViewsClient implements IMetricsExplorerViewsClient { + constructor(private readonly http: HttpStart) {} + + async findMetricsExplorerViews(): Promise { + const response = await this.http.get(getMetricsExplorerViewUrl()).catch((error) => { + throw new FetchMetricsExplorerViewError(`Failed to fetch metrics explorer views: ${error}`); + }); + + const { data } = decodeOrThrow( + findMetricsExplorerViewResponsePayloadRT, + (message: string) => + new FetchMetricsExplorerViewError(`Failed to decode metrics explorer views: ${message}"`) + )(response); + + return data; + } + + async getMetricsExplorerView(metricsExplorerViewId: string): Promise { + const response = await this.http + .get(getMetricsExplorerViewUrl(metricsExplorerViewId)) + .catch((error) => { + throw new FetchMetricsExplorerViewError( + `Failed to fetch metrics explorer view "${metricsExplorerViewId}": ${error}` + ); + }); + + const { data } = decodeOrThrow( + metricsExplorerViewResponsePayloadRT, + (message: string) => + new FetchMetricsExplorerViewError( + `Failed to decode metrics explorer view "${metricsExplorerViewId}": ${message}"` + ) + )(response); + + return data; + } + + async createMetricsExplorerView( + metricsExplorerViewAttributes: CreateMetricsExplorerViewAttributesRequestPayload + ): Promise { + const response = await this.http + .post(getMetricsExplorerViewUrl(), { + body: JSON.stringify( + createMetricsExplorerViewRequestPayloadRT.encode({ + attributes: metricsExplorerViewAttributes, + }) + ), + }) + .catch((error) => { + throw new UpsertMetricsExplorerViewError( + `Failed to create new metrics explorer view: ${error}` + ); + }); + + const { data } = decodeOrThrow( + metricsExplorerViewResponsePayloadRT, + (message: string) => + new UpsertMetricsExplorerViewError( + `Failed to decode newly written metrics explorer view: ${message}"` + ) + )(response); + + return data; + } + + async updateMetricsExplorerView( + metricsExplorerViewId: string, + metricsExplorerViewAttributes: UpdateMetricsExplorerViewAttributesRequestPayload + ): Promise { + const response = await this.http + .put(getMetricsExplorerViewUrl(metricsExplorerViewId), { + body: JSON.stringify( + createMetricsExplorerViewRequestPayloadRT.encode({ + attributes: metricsExplorerViewAttributes, + }) + ), + }) + .catch((error) => { + throw new UpsertMetricsExplorerViewError( + `Failed to update metrics explorer view "${metricsExplorerViewId}": ${error}` + ); + }); + + const { data } = decodeOrThrow( + metricsExplorerViewResponsePayloadRT, + (message: string) => + new UpsertMetricsExplorerViewError( + `Failed to decode updated metrics explorer view "${metricsExplorerViewId}": ${message}"` + ) + )(response); + + return data; + } + + deleteMetricsExplorerView(metricsExplorerViewId: string): Promise { + return this.http + .delete(getMetricsExplorerViewUrl(metricsExplorerViewId)) + .then(() => null) + .catch((error) => { + throw new DeleteMetricsExplorerViewError( + `Failed to delete metrics explorer view "${metricsExplorerViewId}": ${error}` + ); + }); + } +} diff --git a/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_service.mock.ts b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_service.mock.ts new file mode 100644 index 0000000000000..ce64beb31d0c2 --- /dev/null +++ b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_service.mock.ts @@ -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 { createMetricsExplorerViewsClientMock } from './metrics_explorer_views_client.mock'; +import { MetricsExplorerViewsServiceStart } from './types'; + +export const createMetricsExplorerViewsServiceStartMock = () => ({ + client: createMetricsExplorerViewsClientMock(), +}); + +export const _ensureTypeCompatibility = (): MetricsExplorerViewsServiceStart => + createMetricsExplorerViewsServiceStartMock(); diff --git a/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_service.ts b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_service.ts new file mode 100644 index 0000000000000..e3777b270eb05 --- /dev/null +++ b/x-pack/plugins/infra/public/services/metrics_explorer_views/metrics_explorer_views_service.ts @@ -0,0 +1,25 @@ +/* + * 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 { MetricsExplorerViewsClient } from './metrics_explorer_views_client'; +import { + MetricsExplorerViewsServiceStartDeps, + MetricsExplorerViewsServiceSetup, + MetricsExplorerViewsServiceStart, +} from './types'; + +export class MetricsExplorerViewsService { + public setup(): MetricsExplorerViewsServiceSetup {} + + public start({ http }: MetricsExplorerViewsServiceStartDeps): MetricsExplorerViewsServiceStart { + const client = new MetricsExplorerViewsClient(http); + + return { + client, + }; + } +} diff --git a/x-pack/plugins/infra/public/services/metrics_explorer_views/types.ts b/x-pack/plugins/infra/public/services/metrics_explorer_views/types.ts new file mode 100644 index 0000000000000..7825d362eb9c5 --- /dev/null +++ b/x-pack/plugins/infra/public/services/metrics_explorer_views/types.ts @@ -0,0 +1,35 @@ +/* + * 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 { HttpStart } from '@kbn/core/public'; +import { + MetricsExplorerView, + MetricsExplorerViewAttributes, +} from '../../../common/metrics_explorer_views'; + +export type MetricsExplorerViewsServiceSetup = void; + +export interface MetricsExplorerViewsServiceStart { + client: IMetricsExplorerViewsClient; +} + +export interface MetricsExplorerViewsServiceStartDeps { + http: HttpStart; +} + +export interface IMetricsExplorerViewsClient { + findMetricsExplorerViews(): Promise; + getMetricsExplorerView(metricsExplorerViewId: string): Promise; + createMetricsExplorerView( + metricsExplorerViewAttributes: Partial + ): Promise; + updateMetricsExplorerView( + metricsExplorerViewId: string, + metricsExplorerViewAttributes: Partial + ): Promise; + deleteMetricsExplorerView(metricsExplorerViewId: string): Promise; +} diff --git a/x-pack/plugins/infra/public/types.ts b/x-pack/plugins/infra/public/types.ts index 7a41cbb3a85d3..4897009c1faeb 100644 --- a/x-pack/plugins/infra/public/types.ts +++ b/x-pack/plugins/infra/public/types.ts @@ -45,6 +45,7 @@ import type { } from './components/infrastructure_node_metrics_tables/shared'; import { InventoryViewsServiceStart } from './services/inventory_views'; import { LogViewsServiceStart } from './services/log_views'; +import { MetricsExplorerViewsServiceStart } from './services/metrics_explorer_views'; import { ITelemetryClient } from './services/telemetry'; // Our own setup and start contract values @@ -53,6 +54,7 @@ export type InfraClientSetupExports = void; export interface InfraClientStartExports { inventoryViews: InventoryViewsServiceStart; logViews: LogViewsServiceStart; + metricsExplorerViews: MetricsExplorerViewsServiceStart; telemetry: ITelemetryClient; ContainerMetricsTable: ( props: UseNodeMetricsTableOptions & Partial