[Infra IU] Disable Metrics Explorer for serverless (#167022)

Closes #163282 

## Summary

This PR:
* Adds a `featureFlags.metricsExplorerEnabled` property to the Infra
plugin config to enable and disable Metrics Explorer depending on the
offering type
* Prevents `MetricsExplorerViewsService` initialization for serveless
based on the feature flag
* Prevents creating Metrics Explorer frontend routes when in serverless
* Prevents registration of the MetricsExplorerViews saved object when in
serverless
* Prevents initialization of the `metrics_explorer_views` API routes
when in serverless

**Trying to access Metrics Explorer in serverless**
<img width="1829" alt="CleanShot 2023-09-22 at 12 59 35@2x"
src="2b039925-0f0b-4c07-be29-bbe910de7a34">

**Trying to access views API**
<img width="1829" alt="CleanShot 2023-09-22 at 13 00 00@2x"
src="15269ec2-becd-4ee3-9b5e-d916df28a7b8">

**`infra/metrics_explorer` API still works as per ticket requirements**
<img width="1829" alt="CleanShot 2023-09-22 at 13 00 06@2x"
src="fb23f912-c6fd-46c8-9084-c17c51e5b064">


## How to test
* Checkout locally
* Enable Infra in `serverless.oblt.yml`: `xpack.infra.enabled: true`
* Run Kibana in serverless mode
* Try accessing `/app/metrics/explorer` route and make sure it's not
available
* Make sure other Infra routes (`/app/metrics/inventory` and
`/app/metrics/hosts`) still load as expected
* In Kibana dev console make sure you get 404 for `GET
kbn:/api/infra/metrics_explorer_views`
* Also check that you don't see `metrics-explorer-view` saved object in
the response for `GET
kbn:/api/kibana/management/saved_objects/_allowed_types`
* Run Kibana in non-serverless mode and make sure Metrics Explorer is
accessible and works as usual

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Mykola Harmash 2023-09-29 11:27:19 +02:00 committed by GitHub
parent 4c1ca7e92f
commit d0a0a1f9e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 239 additions and 62 deletions

View file

@ -250,6 +250,11 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
'xpack.index_management.enableLegacyTemplates (any)',
'xpack.index_management.enableIndexStats (any)',
'xpack.infra.sources.default.fields.message (array)',
/**
* xpack.infra.featureFlags.metricsExplorerEnabled is conditional based on traditional/serverless offering
* and will resolve to (boolean)
*/
'xpack.infra.featureFlags.metricsExplorerEnabled (any)',
'xpack.license_management.ui.enabled (boolean)',
'xpack.maps.preserveDrawingBuffer (boolean)',
'xpack.maps.showMapsInspectorAdapter (boolean)',

View file

@ -9,7 +9,8 @@ import { either } from 'fp-ts/Either';
import { metricsExplorerViewRT } from '../../../metrics_explorer_views';
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 METRICS_EXPLORER_VIEW_URL_ENTITY =
`${METRICS_EXPLORER_VIEW_URL}/{metricsExplorerViewId}` as const;
export const getMetricsExplorerViewUrl = (metricsExplorerViewId?: string) =>
[METRICS_EXPLORER_VIEW_URL, metricsExplorerViewId].filter(Boolean).join('/');

View file

@ -25,10 +25,14 @@ export interface InfraConfig {
};
};
};
featureFlags: {
metricsExplorerEnabled: boolean;
};
}
export const publicConfigKeys = {
sources: true,
featureFlags: true,
} as const;
export type InfraPublicConfigKey = keyof {

View file

@ -13,6 +13,7 @@ import { Router, Routes, Route } from '@kbn/shared-ux-router';
import { AppMountParameters } from '@kbn/core/public';
import { Storage } from '@kbn/kibana-utils-plugin/public';
import '../index.scss';
import { InfraPublicConfig } from '../../common/plugin_config_types';
import { LinkToMetricsPage } from '../pages/link_to/link_to_metrics';
import { InfrastructurePage } from '../pages/metrics';
import { InfraClientStartDeps, InfraClientStartExports } from '../types';
@ -20,6 +21,7 @@ import { RedirectWithQueryParams } from '../utils/redirect_with_query_params';
import { CommonInfraProviders, CoreProviders } from './common_providers';
import { prepareMountElement } from './common_styles';
import { SourceProvider } from '../containers/metrics_source';
import { PluginConfigProvider } from '../containers/plugin_config_context';
export const METRICS_APP_DATA_TEST_SUBJ = 'infraMetricsPage';
@ -27,6 +29,7 @@ export const renderApp = (
core: CoreStart,
plugins: InfraClientStartDeps,
pluginStart: InfraClientStartExports,
pluginConfig: InfraPublicConfig,
{ element, history, setHeaderActionMenu, theme$ }: AppMountParameters
) => {
const storage = new Storage(window.localStorage);
@ -39,6 +42,7 @@ export const renderApp = (
history={history}
plugins={plugins}
pluginStart={pluginStart}
pluginConfig={pluginConfig}
setHeaderActionMenu={setHeaderActionMenu}
storage={storage}
theme$={theme$}
@ -56,11 +60,21 @@ const MetricsApp: React.FC<{
core: CoreStart;
history: History<unknown>;
pluginStart: InfraClientStartExports;
pluginConfig: InfraPublicConfig;
plugins: InfraClientStartDeps;
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
storage: Storage;
theme$: AppMountParameters['theme$'];
}> = ({ core, history, pluginStart, plugins, setHeaderActionMenu, storage, theme$ }) => {
}> = ({
core,
history,
pluginStart,
pluginConfig,
plugins,
setHeaderActionMenu,
storage,
theme$,
}) => {
const uiCapabilities = core.application.capabilities;
return (
@ -74,23 +88,25 @@ const MetricsApp: React.FC<{
observabilityAIAssistant={plugins.observabilityAIAssistant}
>
<SourceProvider sourceId="default">
<Router history={history}>
<Routes>
<Route path="/link-to" component={LinkToMetricsPage} />
{uiCapabilities?.infrastructure?.show && (
<RedirectWithQueryParams from="/" exact={true} to="/inventory" />
)}
{uiCapabilities?.infrastructure?.show && (
<RedirectWithQueryParams from="/snapshot" exact={true} to="/inventory" />
)}
{uiCapabilities?.infrastructure?.show && (
<RedirectWithQueryParams from="/metrics-explorer" exact={true} to="/explorer" />
)}
{uiCapabilities?.infrastructure?.show && (
<Route path="/" component={InfrastructurePage} />
)}
</Routes>
</Router>
<PluginConfigProvider value={pluginConfig}>
<Router history={history}>
<Routes>
<Route path="/link-to" component={LinkToMetricsPage} />
{uiCapabilities?.infrastructure?.show && (
<RedirectWithQueryParams from="/" exact={true} to="/inventory" />
)}
{uiCapabilities?.infrastructure?.show && (
<RedirectWithQueryParams from="/snapshot" exact={true} to="/inventory" />
)}
{uiCapabilities?.infrastructure?.show && (
<RedirectWithQueryParams from="/metrics-explorer" exact={true} to="/explorer" />
)}
{uiCapabilities?.infrastructure?.show && (
<Route path="/" component={InfrastructurePage} />
)}
</Routes>
</Router>
</PluginConfigProvider>
</SourceProvider>
</CommonInfraProviders>
</CoreProviders>

View file

@ -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.
*/
import { renderHook } from '@testing-library/react-hooks';
import React from 'react';
import { PluginConfigProvider, usePluginConfig } from './plugin_config_context';
describe('usePluginConfig()', () => {
it('throws an error if the context value was not set before using the hook', () => {
const { result } = renderHook(() => usePluginConfig());
expect(result.error).not.toEqual(undefined);
});
it('returns the plugin config what was set through the provider', () => {
const config = {
featureFlags: { metricsExplorerEnabled: false },
};
const { result } = renderHook(() => usePluginConfig(), {
wrapper: ({ children }) => {
return <PluginConfigProvider value={config}>{children}</PluginConfigProvider>;
},
});
expect(result.error).toEqual(undefined);
expect(result.current).toEqual(config);
});
});

View file

@ -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 { createContext, useContext } from 'react';
import { InfraPublicConfig } from '../../common/plugin_config_types';
const PluginConfigContext = createContext<InfraPublicConfig | undefined>(undefined);
export const usePluginConfig = (): InfraPublicConfig => {
const context = useContext(PluginConfigContext);
if (context === undefined) {
throw new Error(
'PluginConfigContext value was not initialized. Use context provider to set the value before using it.'
);
}
return context;
};
export const PluginConfigProvider = PluginConfigContext.Provider;

View file

@ -24,10 +24,10 @@ import {
UpdateMetricsExplorerViewAttributesRequestPayload,
} from '../../common/http_api/latest';
import { MetricsExplorerView } from '../../common/metrics_explorer_views';
import { useKibanaContextForPlugin } from './use_kibana';
import { useUrlState } from '../utils/use_url_state';
import { useSavedViewsNotifier } from './use_saved_views_notifier';
import { useSourceContext } from '../containers/metrics_source';
import { useKibanaContextForPlugin } from './use_kibana';
export type UseMetricsExplorerViewsResult = SavedViewResult<
MetricsExplorerView,
@ -44,6 +44,11 @@ const queryKeys = {
export const useMetricsExplorerViews = (): UseMetricsExplorerViewsResult => {
const { metricsExplorerViews } = useKibanaContextForPlugin().services;
if (metricsExplorerViews === undefined) {
throw new Error('MetricsExplorerViews service has not been initialized.');
}
const trackMetric = useUiTracker({ app: 'infra_metrics' });
const queryClient = useQueryClient();

View file

@ -8,7 +8,6 @@
import { i18n } from '@kbn/i18n';
import React, { useContext } from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { Routes, Route } from '@kbn/shared-ux-router';
import { EuiErrorBoundary, EuiHeaderLinks, EuiHeaderLink } from '@elastic/eui';
@ -37,12 +36,14 @@ import { HeaderActionMenuContext } from '../../utils/header_action_menu_provider
import { CreateDerivedIndexPattern, useSourceContext } from '../../containers/metrics_source';
import { NotFoundPage } from '../404';
import { ReactQueryProvider } from '../../containers/react_query_provider';
import { usePluginConfig } from '../../containers/plugin_config_context';
const ADD_DATA_LABEL = i18n.translate('xpack.infra.metricsHeaderAddDataButtonLabel', {
defaultMessage: 'Add data',
});
export const InfrastructurePage = ({ match }: RouteComponentProps) => {
export const InfrastructurePage = () => {
const config = usePluginConfig();
const uiCapabilities = useKibana().services.application?.capabilities;
const { setHeaderActionMenu, theme$ } = useContext(HeaderActionMenuContext);
@ -96,19 +97,21 @@ export const InfrastructurePage = ({ match }: RouteComponentProps) => {
)}
<Routes>
<Route path={'/inventory'} component={SnapshotPage} />
<Route path={'/explorer'}>
<MetricsExplorerOptionsContainer>
<WithMetricsExplorerOptionsUrlState />
{source?.configuration ? (
<PageContent
configuration={source.configuration}
createDerivedIndexPattern={createDerivedIndexPattern}
/>
) : (
<SourceLoadingPage />
)}
</MetricsExplorerOptionsContainer>
</Route>
{config.featureFlags.metricsExplorerEnabled && (
<Route path={'/explorer'}>
<MetricsExplorerOptionsContainer>
<WithMetricsExplorerOptionsUrlState />
{source?.configuration ? (
<PageContent
configuration={source.configuration}
createDerivedIndexPattern={createDerivedIndexPattern}
/>
) : (
<SourceLoadingPage />
)}
</MetricsExplorerOptionsContainer>
</Route>
)}
<Route path="/detail/:type/:node" component={NodeDetail} />
<Route path={'/hosts'} component={HostsLandingPage} />
<Route path={'/settings'} component={MetricsSettingsPage} />
@ -131,10 +134,6 @@ const PageContent = (props: {
const { createDerivedIndexPattern, configuration } = props;
return (
<MetricsExplorerPage
derivedIndexPattern={createDerivedIndexPattern()}
source={configuration}
{...props}
/>
<MetricsExplorerPage derivedIndexPattern={createDerivedIndexPattern()} source={configuration} />
);
};

View file

@ -23,6 +23,7 @@ import { MetricsPageTemplate } from '../page_template';
import { metricsExplorerTitle } from '../../../translations';
import { DerivedIndexPattern } from '../../../containers/metrics_source';
import { SavedViews } from './components/saved_views';
interface MetricsExplorerPageProps {
source: MetricsSourceConfigurationProperties;
derivedIndexPattern: DerivedIndexPattern;

View file

@ -50,7 +50,7 @@ import { getLogsHasDataFetcher, getLogsOverviewDataFetcher } from './utils/logs_
export class Plugin implements InfraClientPluginClass {
public config: InfraPublicConfig;
private inventoryViews: InventoryViewsService;
private metricsExplorerViews: MetricsExplorerViewsService;
private metricsExplorerViews?: MetricsExplorerViewsService;
private telemetry: TelemetryService;
private locators?: InfraLocators;
private kibanaVersion: string;
@ -60,7 +60,9 @@ export class Plugin implements InfraClientPluginClass {
this.config = context.config.get();
this.inventoryViews = new InventoryViewsService();
this.metricsExplorerViews = new MetricsExplorerViewsService();
this.metricsExplorerViews = this.config.featureFlags.metricsExplorerEnabled
? new MetricsExplorerViewsService()
: undefined;
this.telemetry = new TelemetryService();
this.kibanaVersion = context.env.packageInfo.version;
}
@ -105,7 +107,9 @@ export class Plugin implements InfraClientPluginClass {
/** !! Need to be kept in sync with the deepLinks in x-pack/plugins/infra/public/plugin.ts */
const infraEntries = [
{ label: 'Inventory', app: 'metrics', path: '/inventory' },
{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' },
...(this.config.featureFlags.metricsExplorerEnabled
? [{ label: 'Metrics Explorer', app: 'metrics', path: '/explorer' }]
: []),
{ label: 'Hosts', isBetaFeature: true, app: 'metrics', path: '/hosts' },
];
pluginsSetup.observabilityShared.navigation.registerSections(
@ -231,13 +235,17 @@ export class Plugin implements InfraClientPluginClass {
}),
path: '/hosts',
},
{
id: 'metrics-explorer',
title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
defaultMessage: 'Metrics Explorer',
}),
path: '/explorer',
},
...(this.config.featureFlags.metricsExplorerEnabled
? [
{
id: 'metrics-explorer',
title: i18n.translate('xpack.infra.homePage.metricsExplorerTabTitle', {
defaultMessage: 'Metrics Explorer',
}),
path: '/explorer',
},
]
: []),
{
id: 'settings',
title: i18n.translate('xpack.infra.homePage.settingsTabTitle', {
@ -266,6 +274,7 @@ export class Plugin implements InfraClientPluginClass {
coreStart,
{ ...plugins, kibanaVersion: this.kibanaVersion },
pluginStart,
this.config,
params
);
},
@ -313,7 +322,7 @@ export class Plugin implements InfraClientPluginClass {
http: core.http,
});
const metricsExplorerViews = this.metricsExplorerViews.start({
const metricsExplorerViews = this.metricsExplorerViews?.start({
http: core.http,
});

View file

@ -62,7 +62,7 @@ export interface InfraClientSetupExports {
export interface InfraClientStartExports {
inventoryViews: InventoryViewsServiceStart;
metricsExplorerViews: MetricsExplorerViewsServiceStart;
metricsExplorerViews?: MetricsExplorerViewsServiceStart;
telemetry: ITelemetryClient;
locators: InfraLocators;
ContainerMetricsTable: (

View file

@ -34,6 +34,7 @@ interface FrozenIndexParams {
export class KibanaFramework {
public router: IRouter<InfraPluginRequestHandlerContext>;
public plugins: InfraServerPluginSetupDeps;
public config: InfraConfig;
private core: CoreSetup<InfraServerPluginStartDeps>;
constructor(
@ -44,6 +45,7 @@ export class KibanaFramework {
this.router = core.http.createRouter();
this.plugins = plugins;
this.core = core;
this.config = config;
}
public registerRoute<Params = any, Query = any, Body = any, Method extends RouteMethod = any>(

View file

@ -1898,6 +1898,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
inventory: {
compositeSize: 2000,
},
featureFlags: {
metricsExplorerEnabled: true,
},
enabled: true,
sources,
});

View file

@ -125,6 +125,9 @@ const createMockStaticConfiguration = (sources: any): InfraConfig => ({
inventory: {
compositeSize: 2000,
},
featureFlags: {
metricsExplorerEnabled: true,
},
sources,
enabled: true,
});

View file

@ -6,7 +6,7 @@
*/
import { Server } from '@hapi/hapi';
import { schema } from '@kbn/config-schema';
import { schema, offeringBasedSchema } from '@kbn/config-schema';
import {
CoreStart,
Plugin,
@ -80,6 +80,12 @@ export const config: PluginConfigDescriptor<InfraConfig> = {
),
})
),
featureFlags: schema.object({
metricsExplorerEnabled: offeringBasedSchema({
traditional: schema.boolean({ defaultValue: true }),
serverless: schema.boolean({ defaultValue: false }),
}),
}),
}),
deprecations: configDeprecations,
exposeToBrowser: publicConfigKeys,
@ -111,7 +117,7 @@ export class InfraServerPlugin
private logsRules: RulesService;
private metricsRules: RulesService;
private inventoryViews: InventoryViewsService;
private metricsExplorerViews: MetricsExplorerViewsService;
private metricsExplorerViews?: MetricsExplorerViewsService;
constructor(context: PluginInitializerContext<InfraConfig>) {
this.config = context.config.get();
@ -129,9 +135,9 @@ export class InfraServerPlugin
);
this.inventoryViews = new InventoryViewsService(this.logger.get('inventoryViews'));
this.metricsExplorerViews = new MetricsExplorerViewsService(
this.logger.get('metricsExplorerViews')
);
this.metricsExplorerViews = this.config.featureFlags.metricsExplorerEnabled
? new MetricsExplorerViewsService(this.logger.get('metricsExplorerViews'))
: undefined;
}
setup(core: InfraPluginCoreSetup, plugins: InfraServerPluginSetupDeps) {
@ -155,12 +161,14 @@ export class InfraServerPlugin
// Setup infra services
const inventoryViews = this.inventoryViews.setup();
const metricsExplorerViews = this.metricsExplorerViews.setup();
const metricsExplorerViews = this.metricsExplorerViews?.setup();
// Register saved object types
core.savedObjects.registerType(infraSourceConfigurationSavedObjectType);
core.savedObjects.registerType(inventoryViewSavedObjectType);
core.savedObjects.registerType(metricsExplorerViewSavedObjectType);
if (this.config.featureFlags.metricsExplorerEnabled) {
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
@ -255,7 +263,7 @@ export class InfraServerPlugin
savedObjects: core.savedObjects,
});
const metricsExplorerViews = this.metricsExplorerViews.start({
const metricsExplorerViews = this.metricsExplorerViews?.start({
infraSources: this.libs.sources,
savedObjects: core.savedObjects,
});

View file

@ -15,6 +15,13 @@ import {
} from '../../../common/http_api/latest';
import type { InfraBackendLibs } from '../../lib/infra_types';
const NON_STARTED_SERVICE_ERROR = {
statusCode: 500,
body: {
message: `Handler for "POST ${METRICS_EXPLORER_VIEW_URL}" was registered but MetricsViewService has not started.`,
},
};
export const initCreateMetricsExplorerViewRoute = ({
framework,
getStartServices,
@ -31,6 +38,11 @@ export const initCreateMetricsExplorerViewRoute = ({
async (_requestContext, request, response) => {
const { body, query } = request;
const [, , { metricsExplorerViews }] = await getStartServices();
if (metricsExplorerViews === undefined) {
return response.customError(NON_STARTED_SERVICE_ERROR);
}
const metricsExplorerViewsClient = metricsExplorerViews.getScopedClient(request);
try {

View file

@ -13,6 +13,13 @@ import {
} from '../../../common/http_api/latest';
import type { InfraBackendLibs } from '../../lib/infra_types';
const NON_STARTED_SERVICE_ERROR = {
statusCode: 500,
body: {
message: `Handler for "DELETE ${METRICS_EXPLORER_VIEW_URL_ENTITY}" was registered but MetricsViewService has not started.`,
},
};
export const initDeleteMetricsExplorerViewRoute = ({
framework,
getStartServices,
@ -28,6 +35,11 @@ export const initDeleteMetricsExplorerViewRoute = ({
async (_requestContext, request, response) => {
const { params } = request;
const [, , { metricsExplorerViews }] = await getStartServices();
if (metricsExplorerViews === undefined) {
return response.customError(NON_STARTED_SERVICE_ERROR);
}
const metricsExplorerViewsClient = metricsExplorerViews.getScopedClient(request);
try {

View file

@ -13,6 +13,13 @@ import {
} from '../../../common/http_api/latest';
import type { InfraBackendLibs } from '../../lib/infra_types';
const NON_STARTED_SERVICE_ERROR = {
statusCode: 500,
body: {
message: `Handler for "GET ${METRICS_EXPLORER_VIEW_URL}" was registered but MetricsViewService has not started.`,
},
};
export const initFindMetricsExplorerViewRoute = ({
framework,
getStartServices,
@ -28,6 +35,11 @@ export const initFindMetricsExplorerViewRoute = ({
async (_requestContext, request, response) => {
const { query } = request;
const [, , { metricsExplorerViews }] = await getStartServices();
if (metricsExplorerViews === undefined) {
return response.customError(NON_STARTED_SERVICE_ERROR);
}
const metricsExplorerViewsClient = metricsExplorerViews.getScopedClient(request);
try {

View file

@ -15,6 +15,13 @@ import {
} from '../../../common/http_api/latest';
import type { InfraBackendLibs } from '../../lib/infra_types';
const NON_STARTED_SERVICE_ERROR = {
statusCode: 500,
body: {
message: `Handler for "GET ${METRICS_EXPLORER_VIEW_URL_ENTITY}" was registered but MetricsViewService has not started.`,
},
};
export const initGetMetricsExplorerViewRoute = ({
framework,
getStartServices,
@ -31,6 +38,11 @@ export const initGetMetricsExplorerViewRoute = ({
async (_requestContext, request, response) => {
const { params, query } = request;
const [, , { metricsExplorerViews }] = await getStartServices();
if (metricsExplorerViews === undefined) {
return response.customError(NON_STARTED_SERVICE_ERROR);
}
const metricsExplorerViewsClient = metricsExplorerViews.getScopedClient(request);
try {

View file

@ -15,6 +15,10 @@ import { initUpdateMetricsExplorerViewRoute } from './update_metrics_explorer_vi
export const initMetricsExplorerViewRoutes = (
dependencies: Pick<InfraBackendLibs, 'framework' | 'getStartServices'>
) => {
if (!dependencies.framework.config.featureFlags.metricsExplorerEnabled) {
return;
}
initCreateMetricsExplorerViewRoute(dependencies);
initDeleteMetricsExplorerViewRoute(dependencies);
initFindMetricsExplorerViewRoute(dependencies);

View file

@ -16,6 +16,13 @@ import {
} from '../../../common/http_api/latest';
import type { InfraBackendLibs } from '../../lib/infra_types';
const NON_STARTED_SERVICE_ERROR = {
statusCode: 500,
body: {
message: `Handler for "PUT ${METRICS_EXPLORER_VIEW_URL_ENTITY}" was registered but MetricsViewService has not started.`,
},
};
export const initUpdateMetricsExplorerViewRoute = ({
framework,
getStartServices,
@ -33,6 +40,11 @@ export const initUpdateMetricsExplorerViewRoute = ({
async (_requestContext, request, response) => {
const { body, params, query } = request;
const [, , { metricsExplorerViews }] = await getStartServices();
if (metricsExplorerViews === undefined) {
return response.customError(NON_STARTED_SERVICE_ERROR);
}
const metricsExplorerViewsClient = metricsExplorerViews.getScopedClient(request);
try {

View file

@ -27,12 +27,12 @@ export interface InfraPluginSetup {
sourceProperties: InfraStaticSourceConfiguration
) => void;
inventoryViews: InventoryViewsServiceSetup;
metricsExplorerViews: MetricsExplorerViewsServiceSetup;
metricsExplorerViews?: MetricsExplorerViewsServiceSetup;
}
export interface InfraPluginStart {
inventoryViews: InventoryViewsServiceStart;
metricsExplorerViews: MetricsExplorerViewsServiceStart;
metricsExplorerViews?: MetricsExplorerViewsServiceStart;
}
export type MlSystem = ReturnType<MlPluginSetup['mlSystemProvider']>;