Skip to content

Commit

Permalink
[APM] Fix condition for enabling isEntityCentricExperienceView (#1…
Browse files Browse the repository at this point in the history
…88343)

fixes elastic/observability-dev#3736
## Summary

Initially, the new entity experience view was enabled if the entity data
transform was activated.

However, this is not the desired behavior: when reverting to the classic
view, the entity definition was being deleted. Return to the classic
view should only affect the individual user.

- The entity definition resides on the cluster.
- The entity centric experience feature flag is set per space.
- The entity view is set **per browser.**

### Changes
- The new view (`isEntityCentricExperienceViewEnabled`) is now
determined by checking entity manager enablement, feature flag, **and
local storage.**
- Restoring the classic view no longer deletes the data transforms.


https://github.com/user-attachments/assets/e45ba1c8-9cbb-44a1-96fc-68de5ba992c6

---------

Co-authored-by: Jenny <[email protected]>
  • Loading branch information
kpatticha and jennypavlova authored Jul 16, 2024
1 parent 292013e commit 9557e06
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ describe('APM deep links', () => {
});
it('navigates to apm links on search elastic', () => {
cy.visitKibana('/');
cy.getByTestSubj('nav-search-input').type('APM').focus();
cy.getByTestSubj('nav-search-input')
.should('be.visible')
.type('APM', { force: true, delay: 100 })
.focus();
cy.contains('APM');
cy.contains('APM / Services');
cy.contains('APM / Service groups');
Expand All @@ -26,32 +29,44 @@ describe('APM deep links', () => {
cy.contains('APM').click({ force: true });
cy.url().should('include', '/apm/services');

cy.getByTestSubj('nav-search-input').type('APM');
cy.getByTestSubj('nav-search-input')
.should('be.visible')
.type('APM', { force: true, delay: 100 });
// navigates to services page
cy.contains('APM / Services').click({ force: true });
cy.url().should('include', '/apm/services');

cy.getByTestSubj('nav-search-input').type('APM');
cy.getByTestSubj('nav-search-input')
.should('be.visible')
.type('APM', { force: true, delay: 100 });
// navigates to service groups page
cy.contains('APM / Service groups').click({ force: true });
cy.url().should('include', '/apm/service-groups');

cy.getByTestSubj('nav-search-input').type('APM');
cy.getByTestSubj('nav-search-input')
.should('be.visible')
.type('APM', { force: true, delay: 100 });
// navigates to traces page
cy.contains('APM / Traces').click({ force: true });
cy.url().should('include', '/apm/traces');

cy.getByTestSubj('nav-search-input').type('APM');
cy.getByTestSubj('nav-search-input')
.should('be.visible')
.type('APM', { force: true, delay: 100 });
// navigates to service maps
cy.contains('APM / Service Map').click({ force: true });
cy.url().should('include', '/apm/service-map');

cy.getByTestSubj('nav-search-input').type('APM');
cy.getByTestSubj('nav-search-input')
.should('be.visible')
.type('APM', { force: true, delay: 100 });
// navigates to dependencies page
cy.contains('APM / Dependencies').click({ force: true });
cy.url().should('include', '/apm/dependencies/inventory');

cy.getByTestSubj('nav-search-input').type('APM');
cy.getByTestSubj('nav-search-input')
.should('be.visible')
.type('APM', { force: true, delay: 100 });
// navigates to settings page
cy.contains('APM / Settings').click({ force: true });
cy.url().should('include', '/apm/settings/general-settings');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
EuiLink,
} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { entityCentricExperience } from '@kbn/observability-plugin/common';
import { i18n } from '@kbn/i18n';
import { isEmpty, sortBy } from 'lodash';
import React, { useState, useCallback } from 'react';
Expand All @@ -25,18 +24,14 @@ import { Sort } from './sort';
import { RefreshServiceGroupsSubscriber } from '../refresh_service_groups_subscriber';
import { ServiceGroupSaveButton } from '../service_group_save';
import { BetaBadge } from '../../../shared/beta_badge';
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
import { useEntityManagerEnablementContext } from '../../../../context/entity_manager_context/use_entity_manager_enablement_context';

export type ServiceGroupsSortType = 'recently_added' | 'alphabetical';

const GET_STARTED_URL = 'https://www.elastic.co/guide/en/apm/get-started/current/index.html';

export function ServiceGroupsList() {
const { core } = useApmPluginContext();
const isEntityCentricExperienceEnabled = core.uiSettings.get<boolean>(
entityCentricExperience,
false
);
const { isEntityCentricExperienceViewEnabled } = useEntityManagerEnablementContext();

const [filter, setFilter] = useState('');

Expand Down Expand Up @@ -141,7 +136,7 @@ export function ServiceGroupsList() {
{i18n.translate('xpack.apm.serviceGroups.listDescription', {
defaultMessage: 'Displayed service counts reflect the last 24 hours.',
})}
{isEntityCentricExperienceEnabled && (
{isEntityCentricExperienceViewEnabled && (
<FormattedMessage
id="xpack.apm.serviceGroups.onlyApm"
defaultMessage="Only showing services {link}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import { useApmParams } from '../../../hooks/use_apm_params';
import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context';

export function ServiceInventory() {
const { isEntityManagerEnabled, isEnablementPending } = useEntityManagerEnablementContext();
const { isEnablementPending, isEntityCentricExperienceViewEnabled } =
useEntityManagerEnablementContext();

const {
query: { serviceGroup },
Expand All @@ -35,7 +36,7 @@ export function ServiceInventory() {
);
}

return isEntityManagerEnabled && isEmpty(serviceGroup) ? (
return isEntityCentricExperienceViewEnabled && isEmpty(serviceGroup) ? (
<MultiSignalInventory />
) : (
<ApmServiceInventory />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export function ApmHeaderActionMenu() {
capabilities
);
const canSaveApmAlerts = capabilities.apm.save && canSaveAlerts;
const { isEntityManagerEnabled, isEnablementPending } = useEntityManagerEnablementContext();
const { isEntityCentricExperienceViewEnabled, isEnablementPending } =
useEntityManagerEnablementContext();

function apmHref(path: string) {
return getLegacyApmHref({ basePath, path, search });
Expand Down Expand Up @@ -72,7 +73,7 @@ export function ApmHeaderActionMenu() {
canReadMlJobs={canReadMlJobs}
/>
)}
{isEntityManagerEnabled ? (
{isEntityCentricExperienceViewEnabled ? (
<AddDataContextMenu />
) : (
<EuiHeaderLink
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { EuiFlexGroup, EuiFlexItem, EuiPageHeaderProps } from '@elastic/eui';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import { entityCentricExperience } from '@kbn/observability-plugin/common';
import { ObservabilityPageTemplateProps } from '@kbn/observability-shared-plugin/public';
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
import React, { useContext } from 'react';
Expand Down Expand Up @@ -70,8 +71,12 @@ export function ApmMainTemplate({
const { http, docLinks, observabilityShared, application } = services;
const { kibanaVersion, isCloudEnv, isServerlessEnv } = kibanaEnvironment;
const basePath = http?.basePath.get();
const { config } = useApmPluginContext();
const { isEntityManagerEnabled } = useEntityManagerEnablementContext();
const { config, core } = useApmPluginContext();
const isEntityCentricExperienceSettingEnabled = core.uiSettings.get<boolean>(
entityCentricExperience,
false
);
const { isEntityCentricExperienceViewEnabled } = useEntityManagerEnablementContext();

const ObservabilityPageTemplate = observabilityShared.navigation.PageTemplate;

Expand Down Expand Up @@ -139,7 +144,7 @@ export function ApmMainTemplate({
<FeatureFeedbackButton
data-test-subj="infraApmFeedbackLink"
formUrl={
isEntityManagerEnabled && sanitizedPath.includes('service')
isEntityCentricExperienceViewEnabled && sanitizedPath.includes('service')
? APM_NEW_EXPERIENCE_FEEDBACK_LINK
: APM_FEEDBACK_LINK
}
Expand All @@ -165,7 +170,11 @@ export function ApmMainTemplate({
pageTitle: pageHeaderTitle,
children: (
<EuiFlexGroup direction="column">
{showEnablementCallout && selectedNavButton === 'allServices' && <EntityEnablement />}
{isEntityCentricExperienceSettingEnabled &&
showEnablementCallout &&
selectedNavButton === 'allServices' ? (
<EntityEnablement />
) : null}
{showServiceGroupsNav && selectedNavButton && (
<ServiceGroupsButtonGroup selectedNavButton={selectedNavButton} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {
EuiSkeletonTitle,
EuiIcon,
} from '@elastic/eui';
import { entityCentricExperience } from '@kbn/observability-plugin/common';
import React from 'react';
import { i18n } from '@kbn/i18n';
import type { KibanaPageTemplateProps } from '@kbn/shared-ux-page-kibana-template';
Expand All @@ -21,8 +20,8 @@ import { useApmRouter } from '../../../hooks/use_apm_router';
import { useAnyOfApmParams } from '../../../hooks/use_apm_params';
import { ApmMainTemplate } from './apm_main_template';
import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb';
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
import { TechnicalPreviewBadge } from '../../shared/technical_preview_badge';
import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context';

export function ServiceGroupTemplate({
pageTitle,
Expand Down Expand Up @@ -153,11 +152,7 @@ type ServiceGroupContextTab = NonNullable<EuiPageHeaderProps['tabs']>[0] & {
function useTabs(selectedTab: ServiceGroupContextTab['key']) {
const router = useApmRouter();
const { query } = useAnyOfApmParams('/services', '/service-map');
const { core } = useApmPluginContext();
const isEntityCentricExperienceEnabled = core.uiSettings.get<boolean>(
entityCentricExperience,
false
);
const { isEntityCentricExperienceViewEnabled } = useEntityManagerEnablementContext();

const tabs: ServiceGroupContextTab[] = [
{
Expand All @@ -170,7 +165,7 @@ function useTabs(selectedTab: ServiceGroupContextTab['key']) {
})}
</EuiFlexItem>
<EuiFlexItem grow={false}>
{isEntityCentricExperienceEnabled && (
{isEntityCentricExperienceViewEnabled && (
<TechnicalPreviewBadge icon="beaker" style={{ verticalAlign: 'middle' }} />
)}
</EuiFlexItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { TechnicalPreviewBadge } from '../technical_preview_badge';
import { ApmPluginStartDeps } from '../../../plugin';
import { useEntityManagerEnablementContext } from '../../../context/entity_manager_context/use_entity_manager_enablement_context';
import { FeedbackModal } from './feedback_modal';
import { ServiceInventoryView } from '../../../context/entity_manager_context/entity_manager_context';
import { Unauthorized } from './unauthorized_modal';

export function EntityEnablement() {
Expand All @@ -35,25 +36,19 @@ export function EntityEnablement() {
services: { entityManager },
} = useKibana<ApmPluginStartDeps>();

const { isEntityManagerEnabled, isEnablementPending, refetch } =
useEntityManagerEnablementContext();
const {
isEnablementPending,
refetch,
setServiceInventoryViewLocalStorageSetting,
isEntityCentricExperienceViewEnabled,
} = useEntityManagerEnablementContext();

const [isPopoverOpen, togglePopover] = useToggle(false);
const [isLoading, setIsLoading] = useToggle(false);

const handleRestoreView = async () => {
setIsLoading(true);
try {
const response = await entityManager.entityClient.disableManagedEntityDiscovery();
if (response.success) {
setIsLoading(false);
setsIsFeedbackModalVisible(true);
}
} catch (error) {
setIsLoading(false);
setsIsFeedbackModalVisible(true);
console.error(error);
}
setServiceInventoryViewLocalStorageSetting(ServiceInventoryView.classic);
setsIsFeedbackModalVisible(true);
};

const handleEnablement = async () => {
Expand All @@ -62,6 +57,7 @@ export function EntityEnablement() {
const response = await entityManager.entityClient.enableManagedEntityDiscovery();
if (response.success) {
setIsLoading(false);
setServiceInventoryViewLocalStorageSetting(ServiceInventoryView.entity);
refetch();
}

Expand All @@ -77,7 +73,6 @@ export function EntityEnablement() {

const handleOnCloseFeedback = () => {
setsIsFeedbackModalVisible(false);
refetch();
};

return isEnablementPending ? (
Expand All @@ -91,11 +86,11 @@ export function EntityEnablement() {
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiLink
disabled={isEntityManagerEnabled}
disabled={isEntityCentricExperienceViewEnabled}
data-test-subj="tryOutEEMLink"
onClick={handleEnablement}
>
{isEntityManagerEnabled
{isEntityCentricExperienceViewEnabled
? i18n.translate('xpack.apm.eemEnablement.enabled.', {
defaultMessage: 'Viewing our new experience',
})
Expand Down Expand Up @@ -147,7 +142,7 @@ export function EntityEnablement() {
</EuiPopoverFooter>
</EuiPopover>
</EuiFlexItem>
{isEntityManagerEnabled && (
{isEntityCentricExperienceViewEnabled && (
<EuiFlexItem grow={false}>
<EuiLink data-test-subj="restoreClassicView" onClick={handleRestoreView}>
{i18n.translate('xpack.apm.eemEnablement.restoreClassicView.', {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,28 @@
* 2.0.
*/
import React, { createContext } from 'react';
import { entityCentricExperience } from '@kbn/observability-plugin/common';
import { ENTITY_FETCH_STATUS, useEntityManager } from '../../hooks/use_entity_manager';
import { useLocalStorage } from '../../hooks/use_local_storage';
import { useApmPluginContext } from '../apm_plugin/use_apm_plugin_context';

export interface EntityManagerEnablementContextValue {
isEntityManagerEnabled: boolean;
entityManagerEnablementStatus: ENTITY_FETCH_STATUS;
isEnablementPending: boolean;
refetch: () => void;
serviceInventoryViewLocalStorageSetting: ServiceInventoryView;
setServiceInventoryViewLocalStorageSetting: (view: ServiceInventoryView) => void;
isEntityCentricExperienceViewEnabled: boolean;
}

export enum ServiceInventoryView {
classic = 'classic',
entity = 'entity',
}

export const serviceInventoryStorageKey = 'apm.service.inventory.view';

export const EntityManagerEnablementContext = createContext(
{} as EntityManagerEnablementContextValue
);
Expand All @@ -23,15 +36,32 @@ export function EntityManagerEnablementContextProvider({
}: {
children: React.ReactChild;
}) {
const { isEnabled, status, refetch } = useEntityManager();
const { core } = useApmPluginContext();
const { isEnabled: isEntityManagerEnabled, status, refetch } = useEntityManager();

const [serviceInventoryViewLocalStorageSetting, setServiceInventoryViewLocalStorageSetting] =
useLocalStorage(serviceInventoryStorageKey, ServiceInventoryView.classic);

const isEntityCentricExperienceSettingEnabled = core.uiSettings.get<boolean>(
entityCentricExperience,
false
);

const isEntityCentricExperienceViewEnabled =
isEntityManagerEnabled &&
serviceInventoryViewLocalStorageSetting === ServiceInventoryView.entity &&
isEntityCentricExperienceSettingEnabled;

return (
<EntityManagerEnablementContext.Provider
value={{
isEntityManagerEnabled: isEnabled,
isEntityManagerEnabled,
entityManagerEnablementStatus: status,
isEnablementPending: status === ENTITY_FETCH_STATUS.LOADING,
refetch,
serviceInventoryViewLocalStorageSetting,
setServiceInventoryViewLocalStorageSetting,
isEntityCentricExperienceViewEnabled,
}}
>
{children}
Expand Down

0 comments on commit 9557e06

Please sign in to comment.