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