diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.stories.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.stories.tsx new file mode 100644 index 000000000000..9c7e6803e445 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.stories.tsx @@ -0,0 +1,57 @@ +/* + * 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 type { Story, StoryContext } from '@storybook/react'; +import React, { ComponentType } from 'react'; +import { CoreStart } from '../../../../../../../../src/core/public'; +import { createKibanaReactContext } from '../../../../../../../../src/plugins/kibana_react/public'; +import { APMServiceContext } from '../../../../context/apm_service/apm_service_context'; +import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; +import { AnalyzeDataButton } from './analyze_data_button'; + +const KibanaContext = createKibanaReactContext(({ + http: { basePath: { get: () => '' } }, +} as unknown) as Partial); + +interface Args { + agentName: string; + environment?: string; + serviceName: string; +} + +export default { + title: 'routing/templates/ApmServiceTemplate/AnalyzeDataButton', + component: AnalyzeDataButton, + decorators: [ + (StoryComponent: ComponentType, { args }: StoryContext) => { + const { agentName, environment, serviceName } = args; + + return ( + + + + + + + + ); + }, + ], +}; + +export const Example: Story = () => { + return ; +}; +Example.args = { + agentName: 'iOS/swift', + environment: 'testEnvironment', + serviceName: 'testServiceName', +}; diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.test.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.test.tsx new file mode 100644 index 000000000000..fdd28fdb378b --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.test.tsx @@ -0,0 +1,77 @@ +/* + * 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 { composeStories } from '@storybook/testing-react'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; +import { + ENVIRONMENT_ALL, + ENVIRONMENT_NOT_DEFINED, +} from '../../../../../common/environment_filter_values'; +import * as stories from './analyze_data_button.stories'; + +const { Example } = composeStories(stories); + +describe('AnalyzeDataButton', () => { + describe('with a non-RUM and non-mobile agent', () => { + it('renders nothing', () => { + render(); + + expect(screen.queryByRole('link')).not.toBeInTheDocument(); + }); + }); + + describe('with a RUM agent', () => { + it('uses a ux dataType', () => { + render(); + + expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( + 'http://localhost/app/observability/exploratory-view#?sr=(apm-series:(dt:ux,isNew:!t,op:average,rdf:(service.environment:!(testEnvironment),service.name:!(testServiceName)),rt:kpi-over-time,time:(from:now-15m,to:now)))' + ); + }); + }); + + describe('with a mobile agent', () => { + it('uses a mobile dataType', () => { + render(); + + expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( + 'http://localhost/app/observability/exploratory-view#?sr=(apm-series:(dt:mobile,isNew:!t,op:average,rdf:(service.environment:!(testEnvironment),service.name:!(testServiceName)),rt:kpi-over-time,time:(from:now-15m,to:now)))' + ); + }); + }); + + describe('with no environment', () => { + it('does not include the environment', () => { + render(); + + expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( + 'http://localhost/app/observability/exploratory-view#?sr=(apm-series:(dt:mobile,isNew:!t,op:average,rdf:(service.name:!(testServiceName)),rt:kpi-over-time,time:(from:now-15m,to:now)))' + ); + }); + }); + + describe('with environment not defined', () => { + it('does not include the environment', () => { + render(); + + expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( + 'http://localhost/app/observability/exploratory-view#?sr=(apm-series:(dt:mobile,isNew:!t,op:average,rdf:(service.name:!(testServiceName)),rt:kpi-over-time,time:(from:now-15m,to:now)))' + ); + }); + }); + + describe('with environment all', () => { + it('uses ALL_VALUES', () => { + render(); + + expect((screen.getByRole('link') as HTMLAnchorElement).href).toEqual( + 'http://localhost/app/observability/exploratory-view#?sr=(apm-series:(dt:mobile,isNew:!t,op:average,rdf:(service.environment:!(ALL_VALUES),service.name:!(testServiceName)),rt:kpi-over-time,time:(from:now-15m,to:now)))' + ); + }); + }); +}); diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx new file mode 100644 index 000000000000..e96334936444 --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/analyze_data_button.tsx @@ -0,0 +1,87 @@ +/* + * 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, EuiToolTip } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import React from 'react'; +import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; +import { + createExploratoryViewUrl, + SeriesUrl, +} from '../../../../../../observability/public'; +import { ALL_VALUES_SELECTED } from '../../../../../../observability/public'; +import { + isIosAgentName, + isRumAgentName, +} from '../../../../../common/agent_name'; +import { + SERVICE_ENVIRONMENT, + SERVICE_NAME, +} from '../../../../../common/elasticsearch_fieldnames'; +import { + ENVIRONMENT_ALL, + ENVIRONMENT_NOT_DEFINED, +} from '../../../../../common/environment_filter_values'; +import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; +import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; + +function getEnvironmentDefinition(environment?: string) { + switch (environment) { + case ENVIRONMENT_ALL.value: + return { [SERVICE_ENVIRONMENT]: [ALL_VALUES_SELECTED] }; + case ENVIRONMENT_NOT_DEFINED.value: + case undefined: + return {}; + default: + return { [SERVICE_ENVIRONMENT]: [environment] }; + } +} + +export function AnalyzeDataButton() { + const { agentName, serviceName } = useApmServiceContext(); + const { services } = useKibana(); + const { urlParams } = useUrlParams(); + const { rangeTo, rangeFrom, environment } = urlParams; + const basepath = services.http?.basePath.get(); + + if (isRumAgentName(agentName) || isIosAgentName(agentName)) { + const href = createExploratoryViewUrl( + { + 'apm-series': { + dataType: isRumAgentName(agentName) ? 'ux' : 'mobile', + time: { from: rangeFrom, to: rangeTo }, + reportType: 'kpi-over-time', + reportDefinitions: { + [SERVICE_NAME]: [serviceName], + ...getEnvironmentDefinition(environment), + }, + operationType: 'average', + isNew: true, + } as SeriesUrl, + }, + basepath + ); + + return ( + + + {i18n.translate('xpack.apm.analyzeDataButton.label', { + defaultMessage: 'Analyze data', + })} + + + ); + } + + return null; +} diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx similarity index 66% rename from x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx rename to x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx index 2e10c853f542..591ccae32f61 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template/index.tsx @@ -5,45 +5,33 @@ * 2.0. */ -import React from 'react'; -import { i18n } from '@kbn/i18n'; import { + EuiBetaBadge, EuiFlexGroup, EuiFlexItem, EuiPageHeaderProps, EuiTitle, - EuiBetaBadge, - EuiToolTip, - EuiButtonEmpty, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { omit } from 'lodash'; -import { ApmMainTemplate } from './apm_main_template'; -import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; -import { ApmServiceContextProvider } from '../../../context/apm_service/apm_service_context'; -import { enableServiceOverview } from '../../../../common/ui_settings_keys'; +import React from 'react'; import { + isIosAgentName, isJavaAgentName, isRumAgentName, - isIosAgentName, -} from '../../../../common/agent_name'; -import { ServiceIcons } from '../../shared/service_icons'; -import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; -import { useUrlParams } from '../../../context/url_params_context/use_url_params'; -import { ENVIRONMENT_NOT_DEFINED } from '../../../../common/environment_filter_values'; -import { - SERVICE_NAME, - SERVICE_ENVIRONMENT, -} from '../../../../common/elasticsearch_fieldnames'; -import { Correlations } from '../../app/correlations'; -import { SearchBar } from '../../shared/search_bar'; -import { - createExploratoryViewUrl, - SeriesUrl, -} from '../../../../../observability/public'; -import { useApmParams } from '../../../hooks/use_apm_params'; -import { useBreadcrumb } from '../../../context/breadcrumbs/use_breadcrumb'; -import { useApmRouter } from '../../../hooks/use_apm_router'; +} from '../../../../../common/agent_name'; +import { enableServiceOverview } from '../../../../../common/ui_settings_keys'; +import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; +import { ApmServiceContextProvider } from '../../../../context/apm_service/apm_service_context'; +import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; +import { useBreadcrumb } from '../../../../context/breadcrumbs/use_breadcrumb'; +import { useApmParams } from '../../../../hooks/use_apm_params'; +import { useApmRouter } from '../../../../hooks/use_apm_router'; +import { Correlations } from '../../../app/correlations'; +import { SearchBar } from '../../../shared/search_bar'; +import { ServiceIcons } from '../../../shared/service_icons'; +import { ApmMainTemplate } from '../apm_main_template'; +import { AnalyzeDataButton } from './analyze_data_button'; type Tab = NonNullable[0] & { key: @@ -105,7 +93,7 @@ function TemplateWithContext({ -

{serviceName}

+ <>{serviceName}
@@ -115,7 +103,7 @@ function TemplateWithContext({ - + @@ -132,53 +120,6 @@ function TemplateWithContext({ ); } -function AnalyzeDataButton({ serviceName }: { serviceName: string }) { - const { agentName } = useApmServiceContext(); - const { services } = useKibana(); - const { urlParams } = useUrlParams(); - const { rangeTo, rangeFrom, environment } = urlParams; - const basepath = services.http?.basePath.get(); - - if (isRumAgentName(agentName) || isIosAgentName(agentName)) { - const href = createExploratoryViewUrl( - { - 'apm-series': { - dataType: isRumAgentName(agentName) ? 'ux' : 'mobile', - time: { from: rangeFrom, to: rangeTo }, - reportType: 'kpi-over-time', - reportDefinitions: { - [SERVICE_NAME]: [serviceName], - ...(!!environment && ENVIRONMENT_NOT_DEFINED.value !== environment - ? { [SERVICE_ENVIRONMENT]: [environment] } - : {}), - }, - operationType: 'average', - isNew: true, - } as SeriesUrl, - }, - basepath - ); - - return ( - - - {i18n.translate('xpack.apm.analyzeDataButton.label', { - defaultMessage: 'Analyze data', - })} - - - ); - } - - return null; -} - function useTabs({ selectedTab }: { selectedTab: Tab['key'] }) { const { agentName } = useApmServiceContext(); const { core, config } = useApmPluginContext(); diff --git a/x-pack/plugins/observability/public/index.ts b/x-pack/plugins/observability/public/index.ts index 6bafe465fd02..cb390be635e1 100644 --- a/x-pack/plugins/observability/public/index.ts +++ b/x-pack/plugins/observability/public/index.ts @@ -65,6 +65,7 @@ export { useBreadcrumbs } from './hooks/use_breadcrumbs'; export { useTheme } from './hooks/use_theme'; export { getApmTraceUrl } from './utils/get_apm_trace_url'; export { createExploratoryViewUrl } from './components/shared/exploratory_view/configurations/utils'; +export { ALL_VALUES_SELECTED } from './components/shared/field_value_suggestions/field_value_combobox'; export { FilterValueLabel } from './components/shared/filter_value_label/filter_value_label'; export type { SeriesUrl } from './components/shared/exploratory_view/types';