Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Put APM links into header action menu #82292

Merged
merged 33 commits into from
Nov 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1403175
WIP header changes
smith Oct 30, 2020
2785684
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 1, 2020
c83b447
Roll back some changes
smith Nov 1, 2020
1756469
WIP header links
smith Nov 2, 2020
c388a1e
wat
smith Nov 2, 2020
2486fec
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 2, 2020
e3809db
more mucking about on apm and update obs
smith Nov 2, 2020
05f9650
revert some chagnes
smith Nov 2, 2020
dea6d00
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 3, 2020
bc8ef9a
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 3, 2020
be5740c
More changes
smith Nov 4, 2020
34575f3
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 4, 2020
ca75e31
useservicename
smith Nov 5, 2020
a96355f
more working
smith Nov 5, 2020
386d5c6
Type fixes
smith Nov 5, 2020
2f3b452
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 5, 2020
75eec9e
Loading spinner
smith Nov 5, 2020
2a4ba7d
i18n fix
smith Nov 5, 2020
2b30444
fixes
smith Nov 5, 2020
e8e0fb0
i18n fix
smith Nov 5, 2020
7b574fe
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 5, 2020
8013c89
Merge branch 'master' into nls/header-links
kibanamachine Nov 6, 2020
6c6db71
Merge branch 'master' into nls/header-links
kibanamachine Nov 8, 2020
7635ef8
reverse portal in apm
smith Nov 9, 2020
fb51de3
rever useservicename
smith Nov 9, 2020
ee737f9
Merge remote-tracking branch 'upstream/master' into nls/header-links
smith Nov 9, 2020
6670f78
working except for obs and unmount
smith Nov 9, 2020
4e6fa5d
component-only menu for apm
smith Nov 9, 2020
887e7ad
update obs
smith Nov 10, 2020
0b5d771
test fixes
smith Nov 10, 2020
297a521
remove commented out code
smith Nov 10, 2020
dfc4571
Fix stories
smith Nov 10, 2020
3e2ac3a
Merge branch 'master' into nls/header-links
kibanamachine Nov 10, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
*/

import {
EuiButtonEmpty,
EuiContextMenu,
EuiContextMenuPanelDescriptor,
EuiHeaderLink,
EuiPopover,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React, { useState } from 'react';
import { AlertType } from '../../../../../common/alert_types';
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
import { AlertingFlyout } from '../../../alerting/AlertingFlyout';
import { IBasePath } from '../../../../../../src/core/public';
import { AlertType } from '../../../common/alert_types';
import { AlertingFlyout } from '../../components/alerting/AlertingFlyout';

const alertLabel = i18n.translate('xpack.apm.home.alertsMenu.alerts', {
defaultMessage: 'Alerts',
Expand Down Expand Up @@ -46,28 +46,32 @@ const CREATE_TRANSACTION_ERROR_RATE_ALERT_PANEL_ID =
const CREATE_ERROR_COUNT_ALERT_PANEL_ID = 'create_error_count_panel';

interface Props {
basePath: IBasePath;
canReadAlerts: boolean;
canSaveAlerts: boolean;
canReadAnomalies: boolean;
includeTransactionDuration: boolean;
}

export function AlertingPopoverAndFlyout(props: Props) {
const { canSaveAlerts, canReadAlerts, canReadAnomalies } = props;

const plugin = useApmPluginContext();

export function AlertingPopoverAndFlyout({
basePath,
canSaveAlerts,
canReadAlerts,
canReadAnomalies,
includeTransactionDuration,
}: Props) {
const [popoverOpen, setPopoverOpen] = useState(false);

const [alertType, setAlertType] = useState<AlertType | null>(null);

const button = (
<EuiButtonEmpty
<EuiHeaderLink
color="primary"
iconType="arrowDown"
iconSide="right"
onClick={() => setPopoverOpen(true)}
onClick={() => setPopoverOpen((prevState) => !prevState)}
>
{alertLabel}
</EuiButtonEmpty>
</EuiHeaderLink>
);

const panels: EuiContextMenuPanelDescriptor[] = [
Expand Down Expand Up @@ -98,7 +102,7 @@ export function AlertingPopoverAndFlyout(props: Props) {
'xpack.apm.home.alertsMenu.viewActiveAlerts',
{ defaultMessage: 'View active alerts' }
),
href: plugin.core.http.basePath.prepend(
href: basePath.prepend(
'/app/management/insightsAndAlerting/triggersActions/alerts'
),
icon: 'tableOfContents',
Expand All @@ -113,6 +117,19 @@ export function AlertingPopoverAndFlyout(props: Props) {
id: CREATE_TRANSACTION_DURATION_ALERT_PANEL_ID,
title: transactionDurationLabel,
items: [
// threshold alerts
...(includeTransactionDuration
? [
{
name: createThresholdAlertLabel,
onClick: () => {
setAlertType(AlertType.TransactionDuration);
setPopoverOpen(false);
},
},
]
: []),

// anomaly alerts
...(canReadAnomalies
? [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import { MissingJobsAlert } from './AnomalyDetectionSetupLink';
import * as hooks from '../../../../hooks/useFetcher';
import { MissingJobsAlert } from './anomaly_detection_setup_link';
import * as hooks from '../../hooks/useFetcher';

async function renderTooltipAnchor({
jobs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,25 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { EuiButtonEmpty, EuiToolTip, EuiIcon } from '@elastic/eui';
import {
EuiHeaderLink,
EuiIcon,
EuiLoadingSpinner,
EuiToolTip,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { useApmPluginContext } from '../../../../hooks/useApmPluginContext';
import { APIReturnType } from '../../../../services/rest/createCallApmApi';
import { APMLink } from './APMLink';
import React from 'react';
import {
ENVIRONMENT_ALL,
getEnvironmentLabel,
} from '../../../../../common/environment_filter_values';
import { useUrlParams } from '../../../../hooks/useUrlParams';
import { useFetcher, FETCH_STATUS } from '../../../../hooks/useFetcher';
import { useLicense } from '../../../../hooks/useLicense';
} from '../../../common/environment_filter_values';
import { getAPMHref } from '../../components/shared/Links/apm/APMLink';
import { useApmPluginContext } from '../../hooks/useApmPluginContext';
import { FETCH_STATUS, useFetcher } from '../../hooks/useFetcher';
import { useLicense } from '../../hooks/useLicense';
import { useUrlParams } from '../../hooks/useUrlParams';
import { APIReturnType } from '../../services/rest/createCallApmApi';
import { units } from '../../style/variables';

export type AnomalyDetectionApiResponse = APIReturnType<
'/api/apm/settings/anomaly-detection',
Expand All @@ -27,24 +33,27 @@ const DEFAULT_DATA = { jobs: [], hasLegacyJobs: false };
export function AnomalyDetectionSetupLink() {
const { uiFilters } = useUrlParams();
const environment = uiFilters.environment;
const plugin = useApmPluginContext();
const canGetJobs = !!plugin.core.application.capabilities.ml?.canGetJobs;
const { core } = useApmPluginContext();
const canGetJobs = !!core.application.capabilities.ml?.canGetJobs;
const license = useLicense();
const hasValidLicense = license?.isActive && license?.hasAtLeast('platinum');
const { basePath } = core.http;

return (
<APMLink
path="/settings/anomaly-detection"
<EuiHeaderLink
color="primary"
href={getAPMHref({ basePath, path: '/settings/anomaly-detection' })}
style={{ whiteSpace: 'nowrap' }}
>
<EuiButtonEmpty size="s" color="primary" iconType="inspect">
{ANOMALY_DETECTION_LINK_LABEL}
</EuiButtonEmpty>

{canGetJobs && hasValidLicense ? (
<MissingJobsAlert environment={environment} />
) : null}
</APMLink>
) : (
<EuiIcon type="inspect" color="primary" />
)}
<span style={{ marginInlineStart: units.half }}>
{ANOMALY_DETECTION_LINK_LABEL}
</span>
</EuiHeaderLink>
);
}

Expand All @@ -56,24 +65,30 @@ export function MissingJobsAlert({ environment }: { environment?: string }) {
{ preservePreviousData: false, showToastOnError: false }
);

const defaultIcon = <EuiIcon type="inspect" color="primary" />;

if (status === FETCH_STATUS.LOADING) {
return <EuiLoadingSpinner />;
}

if (status !== FETCH_STATUS.SUCCESS) {
return null;
return defaultIcon;
}

const isEnvironmentSelected =
environment && environment !== ENVIRONMENT_ALL.value;

// there are jobs for at least one environment
if (!isEnvironmentSelected && data.jobs.length > 0) {
return null;
return defaultIcon;
}

// there are jobs for the selected environment
if (
isEnvironmentSelected &&
data.jobs.some((job) => environment === job.environment)
) {
return null;
return defaultIcon;
}

return (
Expand Down
72 changes: 72 additions & 0 deletions x-pack/plugins/apm/public/application/action_menu/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { EuiHeaderLink, EuiHeaderLinks } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import React from 'react';
import { useParams } from 'react-router-dom';
import { getAlertingCapabilities } from '../../components/alerting/get_alert_capabilities';
import { getAPMHref } from '../../components/shared/Links/apm/APMLink';
import { useApmPluginContext } from '../../hooks/useApmPluginContext';
import { AlertingPopoverAndFlyout } from './alerting_popover_flyout';
import { AnomalyDetectionSetupLink } from './anomaly_detection_setup_link';

export function ActionMenu() {
const { core, plugins } = useApmPluginContext();
const { serviceName } = useParams<{ serviceName?: string }>();
const { search } = window.location;
const { application, http } = core;
const { basePath } = http;
const { capabilities } = application;
const canAccessML = !!capabilities.ml?.canAccessML;
const {
isAlertingAvailable,
canReadAlerts,
canSaveAlerts,
canReadAnomalies,
} = getAlertingCapabilities(plugins, capabilities);

function apmHref(path: string) {
return getAPMHref({ basePath, path, search });
}

function kibanaHref(path: string) {
return basePath.prepend(path);
}

return (
<EuiHeaderLinks>
<EuiHeaderLink
color="primary"
href={apmHref('/settings')}
iconType="gear"
>
{i18n.translate('xpack.apm.settingsLinkLabel', {
defaultMessage: 'Settings',
})}
</EuiHeaderLink>
{isAlertingAvailable && (
<AlertingPopoverAndFlyout
basePath={basePath}
canReadAlerts={canReadAlerts}
canSaveAlerts={canSaveAlerts}
canReadAnomalies={canReadAnomalies}
includeTransactionDuration={serviceName !== undefined}
/>
)}
{canAccessML && <AnomalyDetectionSetupLink />}
<EuiHeaderLink
color="primary"
href={kibanaHref('/app/home#/tutorial/apm')}
iconType="indexOpen"
>
{i18n.translate('xpack.apm.addDataButtonLabel', {
defaultMessage: 'Add data',
})}
</EuiHeaderLink>
</EuiHeaderLinks>
);
}
1 change: 1 addition & 0 deletions x-pack/plugins/apm/public/application/application.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ describe('renderApp', () => {
const params = {
element: document.createElement('div'),
history: createMemoryHistory(),
setHeaderActionMenu: () => {},
};
jest.spyOn(window, 'scrollTo').mockReturnValueOnce(undefined);
createCallApmApi((core.http as unknown) as HttpSetup);
Expand Down
12 changes: 8 additions & 4 deletions x-pack/plugins/apm/public/application/csmApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,23 @@ function CsmApp() {
}

export function CsmAppRoot({
appMountParameters,
core,
deps,
history,
config,
corePlugins: { embeddable },
}: {
appMountParameters: AppMountParameters;
core: CoreStart;
deps: ApmPluginSetupDeps;
history: AppMountParameters['history'];
config: ConfigSchema;
corePlugins: ApmPluginStartDeps;
}) {
const { history } = appMountParameters;
const i18nCore = core.i18n;
const plugins = deps;
const apmPluginContextValue = {
appMountParameters,
config,
core,
plugins,
Expand Down Expand Up @@ -109,10 +111,12 @@ export function CsmAppRoot({
export const renderApp = (
core: CoreStart,
deps: ApmPluginSetupDeps,
{ element, history }: AppMountParameters,
appMountParameters: AppMountParameters,
config: ConfigSchema,
corePlugins: ApmPluginStartDeps
) => {
const { element } = appMountParameters;

createCallApmApi(core.http);

// Automatically creates static index pattern and stores as saved object
Expand All @@ -123,9 +127,9 @@ export const renderApp = (

ReactDOM.render(
<CsmAppRoot
appMountParameters={appMountParameters}
core={core}
deps={deps}
history={history}
config={config}
corePlugins={corePlugins}
/>,
Expand Down
Loading