[APM] Add Experimental Mode for APM UI (#139553)

* experimental tab

* moving apm specific settings

* reverting and addin LazyField

* one setting to rule them all

* renaming tab

* new setting option and flyout

* flyout footer

* refactoring

* creating hook

* removing utils

* saving on kbn adv settings

* saved object

* refactoring

* extracting apm experimental features

* auto subs

* removing auto-subscribe feature

* fixing ci

* removing common settings

* new api to fetch labs items

* renaming

* renaming labs settings

* fixing ci

* handling exception

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* fixing ci

* addressing pr comments

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Cauê Marcondes 2022-09-15 16:28:45 -04:00 committed by GitHub
parent 78f6244109
commit ed6411e813
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 482 additions and 42 deletions

View file

@ -20,6 +20,10 @@ export { ComponentRegistry } from './component_registry';
const LazyField = React.lazy(() => import('./management_app/components/field')); const LazyField = React.lazy(() => import('./management_app/components/field'));
export { LazyField }; export { LazyField };
export { toEditableConfig } from './management_app/lib/to_editable_config';
export function plugin(initializerContext: PluginInitializerContext) { export function plugin(initializerContext: PluginInitializerContext) {
return new AdvancedSettingsPlugin(); return new AdvancedSettingsPlugin();
} }
export type { FieldState } from './management_app/types';

View file

@ -518,6 +518,10 @@ export const stackManagementSchema: MakeSchemaFrom<UsageStats> = {
type: 'boolean', type: 'boolean',
_meta: { description: 'Non-default value of setting.' }, _meta: { description: 'Non-default value of setting.' },
}, },
'observability:apmLabsButton': {
type: 'boolean',
_meta: { description: 'Non-default value of setting.' },
},
'observability:apmProgressiveLoading': { 'observability:apmProgressiveLoading': {
type: 'keyword', type: 'keyword',
_meta: { description: 'Non-default value of setting.' }, _meta: { description: 'Non-default value of setting.' },

View file

@ -139,6 +139,7 @@ export interface UsageStats {
'lens:useFieldExistenceSampling': boolean; 'lens:useFieldExistenceSampling': boolean;
'metrics:allowCheckingForFailedShards': boolean; 'metrics:allowCheckingForFailedShards': boolean;
'observability:apmOperationsTab': boolean; 'observability:apmOperationsTab': boolean;
'observability:apmLabsButton': boolean;
'observability:apmProgressiveLoading': string; 'observability:apmProgressiveLoading': string;
'observability:apmServiceGroupMaxNumberOfServices': number; 'observability:apmServiceGroupMaxNumberOfServices': number;
'observability:apmServiceInventoryOptimizedSorting': boolean; 'observability:apmServiceInventoryOptimizedSorting': boolean;

View file

@ -8622,6 +8622,12 @@
"description": "Non-default value of setting." "description": "Non-default value of setting."
} }
}, },
"observability:apmLabsButton": {
"type": "boolean",
"_meta": {
"description": "Non-default value of setting."
}
},
"observability:apmProgressiveLoading": { "observability:apmProgressiveLoading": {
"type": "keyword", "type": "keyword",
"_meta": { "_meta": {
@ -10257,4 +10263,4 @@
} }
} }
} }
} }

View file

@ -19,7 +19,8 @@
"triggersActionsUi", "triggersActionsUi",
"share", "share",
"unifiedSearch", "unifiedSearch",
"dataViews" "dataViews",
"advancedSettings"
], ],
"optionalPlugins": [ "optionalPlugins": [
"actions", "actions",
@ -48,4 +49,4 @@
"observability", "observability",
"esUiShared" "esUiShared"
] ]
} }

View file

@ -0,0 +1,91 @@
/*
* 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 { EuiButton } from '@elastic/eui';
import { LazyField } from '@kbn/advanced-settings-plugin/public';
import { i18n } from '@kbn/i18n';
import {
apmLabsButton,
apmProgressiveLoading,
apmServiceGroupMaxNumberOfServices,
defaultApmServiceEnvironment,
enableComparisonByDefault,
enableInspectEsQueries,
} from '@kbn/observability-plugin/common';
import { isEmpty } from 'lodash';
import React from 'react';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useApmEditableSettings } from '../../../../hooks/use_apm_editable_settings';
const apmSettingsKeys = [
enableComparisonByDefault,
defaultApmServiceEnvironment,
apmProgressiveLoading,
apmServiceGroupMaxNumberOfServices,
enableInspectEsQueries,
apmLabsButton,
];
export function GeneralSettings() {
const { docLinks, notifications } = useApmPluginContext().core;
const {
handleFieldChange,
settingsEditableConfig,
unsavedChanges,
saveAll,
isSaving,
} = useApmEditableSettings(apmSettingsKeys);
async function handleSave() {
try {
const reloadPage = Object.keys(unsavedChanges).some((key) => {
return settingsEditableConfig[key].requiresPageReload;
});
await saveAll();
if (reloadPage) {
window.location.reload();
}
} catch (e) {
const error = e as Error;
notifications.toasts.addDanger({
title: i18n.translate('xpack.apm.apmSettings.save.error', {
defaultMessage: 'An error occurred while saving the settings',
}),
text: error.message,
});
}
}
return (
<>
{apmSettingsKeys.map((settingKey) => {
const editableConfig = settingsEditableConfig[settingKey];
return (
<LazyField
key={settingKey}
setting={editableConfig}
handleChange={handleFieldChange}
enableSaving
docLinks={docLinks.links}
toasts={notifications.toasts}
unsavedChanges={unsavedChanges[settingKey]}
/>
);
})}
<EuiButton
fill
isLoading={isSaving}
disabled={isEmpty(unsavedChanges)}
onClick={handleSave}
>
{i18n.translate('xpack.apm.labs.reload', {
defaultMessage: 'Reload to apply changes',
})}
</EuiButton>
</>
);
}

View file

@ -20,6 +20,7 @@ import { CustomLinkOverview } from '../../app/settings/custom_link';
import { Schema } from '../../app/settings/schema'; import { Schema } from '../../app/settings/schema';
import { AnomalyDetection } from '../../app/settings/anomaly_detection'; import { AnomalyDetection } from '../../app/settings/anomaly_detection';
import { AgentKeys } from '../../app/settings/agent_keys'; import { AgentKeys } from '../../app/settings/agent_keys';
import { GeneralSettings } from '../../app/settings/general_settings';
function page({ function page({
title, title,
@ -54,6 +55,14 @@ export const settings = {
</Breadcrumb> </Breadcrumb>
), ),
children: { children: {
'/settings/general-settings': page({
title: i18n.translate(
'xpack.apm.views.settings.generalSettings.title',
{ defaultMessage: 'General settings' }
),
element: <GeneralSettings />,
tab: 'general-settings',
}),
'/settings/agent-configuration': page({ '/settings/agent-configuration': page({
tab: 'agent-configuration', tab: 'agent-configuration',
title: i18n.translate( title: i18n.translate(
@ -133,7 +142,7 @@ export const settings = {
tab: 'agent-keys', tab: 'agent-keys',
}), }),
'/settings': { '/settings': {
element: <Redirect to="/settings/agent-configuration" />, element: <Redirect to="/settings/general-settings" />,
}, },
}, },
}, },

View file

@ -8,12 +8,11 @@
import { EuiPageHeaderProps } from '@elastic/eui'; import { EuiPageHeaderProps } from '@elastic/eui';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import React from 'react'; import React from 'react';
import { History } from 'history';
import { useHistory } from 'react-router-dom';
import { CoreStart } from '@kbn/core/public'; import { CoreStart } from '@kbn/core/public';
import { ApmMainTemplate } from './apm_main_template'; import { ApmMainTemplate } from './apm_main_template';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
import { getLegacyApmHref } from '../../shared/links/apm/apm_link'; import { useApmRouter } from '../../../hooks/use_apm_router';
import { ApmRouter } from '../apm_route_config';
type Tab = NonNullable<EuiPageHeaderProps['tabs']>[0] & { type Tab = NonNullable<EuiPageHeaderProps['tabs']>[0] & {
key: key:
@ -22,7 +21,8 @@ type Tab = NonNullable<EuiPageHeaderProps['tabs']>[0] & {
| 'anomaly-detection' | 'anomaly-detection'
| 'apm-indices' | 'apm-indices'
| 'custom-links' | 'custom-links'
| 'schema'; | 'schema'
| 'general-settings';
hidden?: boolean; hidden?: boolean;
}; };
@ -33,8 +33,8 @@ interface Props {
export function SettingsTemplate({ children, selectedTab }: Props) { export function SettingsTemplate({ children, selectedTab }: Props) {
const { core } = useApmPluginContext(); const { core } = useApmPluginContext();
const history = useHistory(); const router = useApmRouter();
const tabs = getTabs({ history, core, selectedTab }); const tabs = getTabs({ core, selectedTab, router });
return ( return (
<ApmMainTemplate <ApmMainTemplate
@ -52,51 +52,44 @@ export function SettingsTemplate({ children, selectedTab }: Props) {
} }
function getTabs({ function getTabs({
history,
core, core,
selectedTab, selectedTab,
router,
}: { }: {
history: History;
core: CoreStart; core: CoreStart;
selectedTab: Tab['key']; selectedTab: Tab['key'];
router: ApmRouter;
}) { }) {
const { basePath } = core.http;
const canAccessML = !!core.application.capabilities.ml?.canAccessML; const canAccessML = !!core.application.capabilities.ml?.canAccessML;
const { search } = history.location;
const tabs: Tab[] = [ const tabs: Tab[] = [
{
key: 'general-settings',
label: i18n.translate('xpack.apm.settings.generalSettings', {
defaultMessage: 'General settings',
}),
href: router.link('/settings/general-settings'),
},
{ {
key: 'agent-configuration', key: 'agent-configuration',
label: i18n.translate('xpack.apm.settings.agentConfig', { label: i18n.translate('xpack.apm.settings.agentConfig', {
defaultMessage: 'Agent Configuration', defaultMessage: 'Agent Configuration',
}), }),
href: getLegacyApmHref({ href: router.link('/settings/agent-configuration'),
basePath,
path: `/settings/agent-configuration`,
search,
}),
}, },
{ {
key: 'agent-keys', key: 'agent-keys',
label: i18n.translate('xpack.apm.settings.agentKeys', { label: i18n.translate('xpack.apm.settings.agentKeys', {
defaultMessage: 'Agent Keys', defaultMessage: 'Agent Keys',
}), }),
href: getLegacyApmHref({ href: router.link('/settings/agent-keys'),
basePath,
path: `/settings/agent-keys`,
search,
}),
}, },
{ {
key: 'anomaly-detection', key: 'anomaly-detection',
label: i18n.translate('xpack.apm.settings.anomalyDetection', { label: i18n.translate('xpack.apm.settings.anomalyDetection', {
defaultMessage: 'Anomaly detection', defaultMessage: 'Anomaly detection',
}), }),
href: getLegacyApmHref({ href: router.link('/settings/anomaly-detection'),
basePath,
path: `/settings/anomaly-detection`,
search,
}),
hidden: !canAccessML, hidden: !canAccessML,
}, },
{ {
@ -104,29 +97,21 @@ function getTabs({
label: i18n.translate('xpack.apm.settings.customizeApp', { label: i18n.translate('xpack.apm.settings.customizeApp', {
defaultMessage: 'Custom Links', defaultMessage: 'Custom Links',
}), }),
href: getLegacyApmHref({ href: router.link('/settings/custom-links'),
basePath,
path: `/settings/custom-links`,
search,
}),
}, },
{ {
key: 'apm-indices', key: 'apm-indices',
label: i18n.translate('xpack.apm.settings.indices', { label: i18n.translate('xpack.apm.settings.indices', {
defaultMessage: 'Indices', defaultMessage: 'Indices',
}), }),
href: getLegacyApmHref({ href: router.link('/settings/apm-indices'),
basePath,
path: `/settings/apm-indices`,
search,
}),
}, },
{ {
key: 'schema', key: 'schema',
label: i18n.translate('xpack.apm.settings.schema', { label: i18n.translate('xpack.apm.settings.schema', {
defaultMessage: 'Schema', defaultMessage: 'Schema',
}), }),
href: getLegacyApmHref({ basePath, path: `/settings/schema`, search }), href: router.link('/settings/schema'),
}, },
]; ];

View file

@ -6,6 +6,7 @@
*/ */
import { EuiHeaderLink, EuiHeaderLinks } from '@elastic/eui'; import { EuiHeaderLink, EuiHeaderLinks } from '@elastic/eui';
import { apmLabsButton } from '@kbn/observability-plugin/common';
import { i18n } from '@kbn/i18n'; import { i18n } from '@kbn/i18n';
import React from 'react'; import React from 'react';
import { getAlertingCapabilities } from '../../alerting/get_alerting_capabilities'; import { getAlertingCapabilities } from '../../alerting/get_alerting_capabilities';
@ -15,6 +16,7 @@ import { AlertingPopoverAndFlyout } from './alerting_popover_flyout';
import { AnomalyDetectionSetupLink } from './anomaly_detection_setup_link'; import { AnomalyDetectionSetupLink } from './anomaly_detection_setup_link';
import { useServiceName } from '../../../hooks/use_service_name'; import { useServiceName } from '../../../hooks/use_service_name';
import { InspectorHeaderLink } from './inspector_header_link'; import { InspectorHeaderLink } from './inspector_header_link';
import { Labs } from './labs';
export function ApmHeaderActionMenu() { export function ApmHeaderActionMenu() {
const { core, plugins } = useApmPluginContext(); const { core, plugins } = useApmPluginContext();
@ -40,8 +42,14 @@ export function ApmHeaderActionMenu() {
return basePath.prepend(path); return basePath.prepend(path);
} }
const isLabsButtonEnabled = core.uiSettings.get<boolean>(
apmLabsButton,
false
);
return ( return (
<EuiHeaderLinks gutterSize="xs"> <EuiHeaderLinks gutterSize="xs">
{isLabsButtonEnabled && <Labs />}
<EuiHeaderLink <EuiHeaderLink
color="text" color="text"
href={apmHref('/storage-explorer')} href={apmHref('/storage-explorer')}

View file

@ -0,0 +1,28 @@
/*
* 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 { EuiButtonEmpty } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { LabsFlyout } from './labs_flyout';
export function Labs() {
const [isOpen, setIsOpen] = useState(false);
function toggleFlyoutVisibility() {
setIsOpen((state) => !state);
}
return (
<>
<EuiButtonEmpty color="text" onClick={toggleFlyoutVisibility}>
{i18n.translate('xpack.apm.labs', { defaultMessage: 'Labs' })}
</EuiButtonEmpty>
{isOpen && <LabsFlyout onClose={toggleFlyoutVisibility} />}
</>
);
}

View file

@ -0,0 +1,147 @@
/*
* 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 {
EuiButton,
EuiButtonEmpty,
EuiFlexGroup,
EuiFlexItem,
EuiFlyout,
EuiFlyoutBody,
EuiFlyoutFooter,
EuiFlyoutHeader,
EuiHorizontalRule,
EuiIcon,
EuiLoadingContent,
EuiTitle,
} from '@elastic/eui';
import { LazyField } from '@kbn/advanced-settings-plugin/public';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useApmEditableSettings } from '../../../../hooks/use_apm_editable_settings';
import { useFetcher, FETCH_STATUS } from '../../../../hooks/use_fetcher';
interface Props {
onClose: () => void;
}
export function LabsFlyout({ onClose }: Props) {
const { docLinks, notifications } = useApmPluginContext().core;
const { data, status } = useFetcher(
(callApmApi) => callApmApi('GET /internal/apm/settings/labs'),
[]
);
const labsItems = data?.labsItems || [];
const {
handleFieldChange,
settingsEditableConfig,
unsavedChanges,
saveAll,
isSaving,
cleanUnsavedChanges,
} = useApmEditableSettings(labsItems);
async function handleSave() {
try {
const reloadPage = Object.keys(unsavedChanges).some((key) => {
return settingsEditableConfig[key].requiresPageReload;
});
await saveAll();
if (reloadPage) {
window.location.reload();
} else {
onClose();
}
} catch (e) {
const error = e as Error;
notifications.toasts.addDanger({
title: i18n.translate('xpack.apm.apmSettings.save.error', {
defaultMessage: 'An error occurred while saving the settings',
}),
text: error.message,
});
}
}
function handelCancel() {
cleanUnsavedChanges();
onClose();
}
const isLoading =
status === FETCH_STATUS.NOT_INITIATED || status === FETCH_STATUS.LOADING;
return (
<EuiFlyout onClose={onClose}>
<EuiFlyoutHeader hasBorder>
<EuiFlexGroup gutterSize="m">
<EuiFlexItem grow={false}>
<EuiIcon type="beaker" size="xl" />
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiTitle>
<h2>
{i18n.translate('xpack.apm.labs', {
defaultMessage: 'Labs',
})}
</h2>
</EuiTitle>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutHeader>
{isLoading ? (
<EuiLoadingContent lines={3} />
) : (
<>
<EuiFlyoutBody>
{labsItems.map((settingKey, i) => {
const editableConfig = settingsEditableConfig[settingKey];
return (
<>
<LazyField
key={settingKey}
setting={editableConfig}
handleChange={handleFieldChange}
enableSaving
docLinks={docLinks.links}
toasts={notifications.toasts}
unsavedChanges={unsavedChanges[settingKey]}
/>
<EuiHorizontalRule />
</>
);
})}
</EuiFlyoutBody>
<EuiFlyoutFooter>
<EuiFlexGroup justifyContent="spaceBetween">
<EuiFlexItem grow={false}>
<EuiButtonEmpty onClick={handelCancel}>
{i18n.translate('xpack.apm.labs.cancel', {
defaultMessage: 'Cancel',
})}
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButton fill isLoading={isSaving} onClick={handleSave}>
{i18n.translate('xpack.apm.labs.reload', {
defaultMessage: 'Reload to apply changes',
})}
</EuiButton>
</EuiFlexItem>
</EuiFlexGroup>
</EuiFlyoutFooter>
</>
)}
</EuiFlyout>
);
}

View file

@ -0,0 +1,104 @@
/*
* 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 { useKibana } from '@kbn/kibana-react-plugin/public';
import { useMemo, useState } from 'react';
import { FieldState } from '@kbn/advanced-settings-plugin/public';
import { toEditableConfig } from '@kbn/advanced-settings-plugin/public';
import { IUiSettingsClient } from '@kbn/core/public';
import { isEmpty } from 'lodash';
function getEditableConfig({
settingsKeys,
uiSettings,
}: {
settingsKeys: string[];
uiSettings?: IUiSettingsClient;
}) {
if (!uiSettings) {
return {};
}
const uiSettingsDefinition = uiSettings.getAll();
const config: Record<string, ReturnType<typeof toEditableConfig>> = {};
settingsKeys.forEach((key) => {
const settingDef = uiSettingsDefinition?.[key];
if (settingDef) {
const editableConfig = toEditableConfig({
def: settingDef,
name: key,
value: settingDef.userValue,
isCustom: uiSettings.isCustom(key),
isOverridden: uiSettings.isOverridden(key),
});
config[key] = editableConfig;
}
});
return config;
}
export function useApmEditableSettings(settingsKeys: string[]) {
const { services } = useKibana();
const { uiSettings } = services;
const [isSaving, setIsSaving] = useState(false);
const [forceReloadSettings, setForceReloadSettings] = useState(0);
const [unsavedChanges, setUnsavedChanges] = useState<
Record<string, FieldState>
>({});
const settingsEditableConfig = useMemo(
() => {
return getEditableConfig({ settingsKeys, uiSettings });
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[uiSettings, settingsKeys, forceReloadSettings]
);
function handleFieldChange(key: string, fieldState: FieldState) {
setUnsavedChanges((state) => {
const newState = { ...state };
const { value, defVal } = settingsEditableConfig[key];
const currentValue = value === undefined ? defVal : value;
if (currentValue === fieldState.value) {
// Delete property from unsaved object if user changes it to the value that was already saved
delete newState[key];
} else {
newState[key] = fieldState;
}
return newState;
});
}
function cleanUnsavedChanges() {
setUnsavedChanges({});
}
async function saveAll() {
if (uiSettings && !isEmpty(unsavedChanges)) {
try {
setIsSaving(true);
const arr = Object.entries(unsavedChanges).map(([key, fieldState]) =>
uiSettings.set(key, fieldState.value)
);
await Promise.all(arr);
setForceReloadSettings((state) => ++state);
cleanUnsavedChanges();
} finally {
setIsSaving(false);
}
}
}
return {
settingsEditableConfig,
unsavedChanges,
handleFieldChange,
saveAll,
isSaving,
cleanUnsavedChanges,
};
}

View file

@ -41,6 +41,7 @@ import { timeRangeMetadataRoute } from '../time_range_metadata/route';
import { traceRouteRepository } from '../traces/route'; import { traceRouteRepository } from '../traces/route';
import { transactionRouteRepository } from '../transactions/route'; import { transactionRouteRepository } from '../transactions/route';
import { storageExplorerRouteRepository } from '../storage_explorer/route'; import { storageExplorerRouteRepository } from '../storage_explorer/route';
import { labsRouteRepository } from '../settings/labs/route';
function getTypedGlobalApmServerRouteRepository() { function getTypedGlobalApmServerRouteRepository() {
const repository = { const repository = {
@ -75,6 +76,7 @@ function getTypedGlobalApmServerRouteRepository() {
...infrastructureRouteRepository, ...infrastructureRouteRepository,
...debugTelemetryRoute, ...debugTelemetryRoute,
...timeRangeMetadataRoute, ...timeRangeMetadataRoute,
...labsRouteRepository,
}; };
return repository; return repository;

View file

@ -0,0 +1,22 @@
/*
* 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 { uiSettings } from '@kbn/observability-plugin/server';
import { createApmServerRoute } from '../../apm_routes/create_apm_server_route';
const getLabsRoute = createApmServerRoute({
endpoint: 'GET /internal/apm/settings/labs',
options: { tags: ['access:apm'] },
handler: async (): Promise<{ labsItems: string[] }> => {
const labsItems = Object.entries(uiSettings)
.filter(([key, value]): boolean | undefined => value.showInLabs)
.map(([key]): string => key);
return { labsItems };
},
});
export const labsRouteRepository = getLabsRoute;

View file

@ -12,15 +12,18 @@ export { formatDurationFromTimeUnitChar } from './utils/formatters';
export { ProcessorEvent } from './processor_event'; export { ProcessorEvent } from './processor_event';
export { export {
enableNewSyntheticsView,
enableInspectEsQueries, enableInspectEsQueries,
maxSuggestions, maxSuggestions,
enableComparisonByDefault, enableComparisonByDefault,
defaultApmServiceEnvironment, defaultApmServiceEnvironment,
apmServiceInventoryOptimizedSorting,
apmProgressiveLoading, apmProgressiveLoading,
enableServiceGroups,
apmServiceInventoryOptimizedSorting,
apmServiceGroupMaxNumberOfServices, apmServiceGroupMaxNumberOfServices,
apmTraceExplorerTab, apmTraceExplorerTab,
apmOperationsTab, apmOperationsTab,
apmLabsButton,
} from './ui_settings_keys'; } from './ui_settings_keys';
export { export {

View file

@ -18,3 +18,4 @@ export const apmServiceGroupMaxNumberOfServices =
'observability:apmServiceGroupMaxNumberOfServices'; 'observability:apmServiceGroupMaxNumberOfServices';
export const apmTraceExplorerTab = 'observability:apmTraceExplorerTab'; export const apmTraceExplorerTab = 'observability:apmTraceExplorerTab';
export const apmOperationsTab = 'observability:apmOperationsTab'; export const apmOperationsTab = 'observability:apmOperationsTab';
export const apmLabsButton = 'observability:apmLabsButton';

View file

@ -51,3 +51,5 @@ export const plugin = (initContext: PluginInitializerContext) =>
export type { Mappings, ObservabilityPluginSetup, ScopedAnnotationsClient }; export type { Mappings, ObservabilityPluginSetup, ScopedAnnotationsClient };
export { createOrUpdateIndex, unwrapEsResponse, WrappedElasticsearchClientError }; export { createOrUpdateIndex, unwrapEsResponse, WrappedElasticsearchClientError };
export { uiSettings } from './ui_settings';

View file

@ -21,6 +21,7 @@ import {
apmServiceGroupMaxNumberOfServices, apmServiceGroupMaxNumberOfServices,
apmTraceExplorerTab, apmTraceExplorerTab,
apmOperationsTab, apmOperationsTab,
apmLabsButton,
} from '../common/ui_settings_keys'; } from '../common/ui_settings_keys';
const technicalPreviewLabel = i18n.translate( const technicalPreviewLabel = i18n.translate(
@ -30,10 +31,12 @@ const technicalPreviewLabel = i18n.translate(
} }
); );
type UiSettings = UiSettingsParams<boolean | number | string> & { showInLabs?: boolean };
/** /**
* uiSettings definitions for Observability. * uiSettings definitions for Observability.
*/ */
export const uiSettings: Record<string, UiSettingsParams<boolean | number | string>> = { export const uiSettings: Record<string, UiSettings> = {
[enableNewSyntheticsView]: { [enableNewSyntheticsView]: {
category: [observabilityFeatureId], category: [observabilityFeatureId],
name: i18n.translate('xpack.observability.enableNewSyntheticsViewExperimentName', { name: i18n.translate('xpack.observability.enableNewSyntheticsViewExperimentName', {
@ -60,6 +63,7 @@ export const uiSettings: Record<string, UiSettingsParams<boolean | number | stri
defaultMessage: 'Inspect Elasticsearch queries in API responses.', defaultMessage: 'Inspect Elasticsearch queries in API responses.',
}), }),
schema: schema.boolean(), schema: schema.boolean(),
requiresPageReload: true,
}, },
[maxSuggestions]: { [maxSuggestions]: {
category: [observabilityFeatureId], category: [observabilityFeatureId],
@ -160,6 +164,7 @@ export const uiSettings: Record<string, UiSettingsParams<boolean | number | stri
}), }),
schema: schema.boolean(), schema: schema.boolean(),
requiresPageReload: true, requiresPageReload: true,
showInLabs: true,
}, },
[apmServiceInventoryOptimizedSorting]: { [apmServiceInventoryOptimizedSorting]: {
category: [observabilityFeatureId], category: [observabilityFeatureId],
@ -178,6 +183,7 @@ export const uiSettings: Record<string, UiSettingsParams<boolean | number | stri
value: false, value: false,
requiresPageReload: false, requiresPageReload: false,
type: 'boolean', type: 'boolean',
showInLabs: true,
}, },
[apmServiceGroupMaxNumberOfServices]: { [apmServiceGroupMaxNumberOfServices]: {
category: [observabilityFeatureId], category: [observabilityFeatureId],
@ -204,6 +210,7 @@ export const uiSettings: Record<string, UiSettingsParams<boolean | number | stri
value: false, value: false,
requiresPageReload: true, requiresPageReload: true,
type: 'boolean', type: 'boolean',
showInLabs: true,
}, },
[apmOperationsTab]: { [apmOperationsTab]: {
category: [observabilityFeatureId], category: [observabilityFeatureId],
@ -219,5 +226,20 @@ export const uiSettings: Record<string, UiSettingsParams<boolean | number | stri
value: false, value: false,
requiresPageReload: true, requiresPageReload: true,
type: 'boolean', type: 'boolean',
showInLabs: true,
},
[apmLabsButton]: {
category: [observabilityFeatureId],
name: i18n.translate('xpack.observability.apmLabs', {
defaultMessage: 'Enable labs button in APM',
}),
description: i18n.translate('xpack.observability.apmLabsDescription', {
defaultMessage:
'This flag determines if the viewer has access to the Labs button, a quick way to enable and disable technical preview features in APM.',
}),
schema: schema.boolean(),
value: false,
requiresPageReload: true,
type: 'boolean',
}, },
}; };