diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.test.tsx deleted file mode 100644 index 4d4991f161f6..000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.test.tsx +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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 { shallow, ShallowWrapper } from 'enzyme'; -import React from 'react'; -import { APMError } from '../../../../../typings/es_schemas/ui/APMError'; -import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction'; -import { IStickyProperty } from '../../../shared/StickyProperties'; -import { StickyErrorProperties } from './StickyErrorProperties'; -import { - ERROR_PAGE_URL, - URL_FULL -} from '../../../../../common/elasticsearch_fieldnames'; - -describe('StickyErrorProperties', () => { - it('should render StickyProperties', () => { - const transaction = { - http: { request: { method: 'GET' } }, - url: { full: 'myUrl' }, - trace: { id: 'traceId' }, - transaction: { - type: 'myTransactionType', - name: 'myTransactionName', - id: 'myTransactionName' - }, - service: { name: 'myService' }, - user: { id: 'myUserId' } - } as Transaction; - - const error = { - '@timestamp': 'myTimestamp', - agent: { name: 'nodejs' }, - http: { request: { method: 'GET' } }, - url: { full: 'myUrl' }, - service: { name: 'myService' }, - user: { id: 'myUserId' }, - error: { exception: [{ handled: true }] }, - transaction: { id: 'myTransactionId', sampled: true } - } as APMError; - - const wrapper = shallow( - - ); - - expect(wrapper).toMatchSnapshot(); - }); - - it('url.full', () => { - const error = { - agent: { name: 'nodejs' }, - url: { full: 'myFullUrl' } - } as APMError; - - const wrapper = shallow( - - ); - const urlValue = getValueByFieldName(wrapper, URL_FULL); - expect(urlValue).toBe('myFullUrl'); - }); - - it('error.page.url', () => { - const error = { - agent: { name: 'rum-js' }, - error: { page: { url: 'myPageUrl' } } - } as APMError; - - const wrapper = shallow( - - ); - const urlValue = getValueByFieldName(wrapper, ERROR_PAGE_URL); - expect(urlValue).toBe('myPageUrl'); - }); - - describe('error.exception.handled', () => { - it('should should render "true"', () => { - const error = { - agent: { name: 'nodejs' }, - error: { exception: [{ handled: true }] } - } as APMError; - const wrapper = shallow( - - ); - const value = getValueByFieldName(wrapper, 'error.exception.handled'); - expect(value).toBe('true'); - }); - - it('should should render "false"', () => { - const error = { - agent: { name: 'nodejs' }, - error: { exception: [{ handled: false }] } - } as APMError; - const wrapper = shallow( - - ); - const value = getValueByFieldName(wrapper, 'error.exception.handled'); - expect(value).toBe('false'); - }); - - it('should should render "N/A"', () => { - const error = { agent: { name: 'nodejs' } } as APMError; - const wrapper = shallow( - - ); - const value = getValueByFieldName(wrapper, 'error.exception.handled'); - expect(value).toBe('N/A'); - }); - }); -}); - -function getValueByFieldName(wrapper: ShallowWrapper, fieldName: string) { - const stickyProps = wrapper.prop('stickyProperties') as IStickyProperty[]; - const prop = stickyProps.find(p => p.fieldName === fieldName); - return prop && prop.val; -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.tsx deleted file mode 100644 index 38856618cc53..000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/StickyErrorProperties.tsx +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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 { i18n } from '@kbn/i18n'; -import { isBoolean } from 'lodash'; -import React, { Fragment } from 'react'; -import { idx } from '@kbn/elastic-idx'; -import { - ERROR_EXC_HANDLED, - HTTP_REQUEST_METHOD, - TRANSACTION_ID, - URL_FULL, - USER_ID, - ERROR_PAGE_URL -} from '../../../../../common/elasticsearch_fieldnames'; -import { NOT_AVAILABLE_LABEL } from '../../../../../common/i18n'; -import { APMError } from '../../../../../typings/es_schemas/ui/APMError'; -import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction'; -import { StickyProperties } from '../../../shared/StickyProperties'; -import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink'; -import { isRumAgentName } from '../../../../../common/agent_name'; - -interface Props { - error: APMError; - transaction: Transaction | undefined; -} - -function TransactionLinkWrapper({ - transaction -}: { - transaction: Transaction | undefined; -}) { - if (!transaction) { - return {NOT_AVAILABLE_LABEL}; - } - - const isSampled = transaction.transaction.sampled; - if (!isSampled) { - return {transaction.transaction.id}; - } - - return ( - - {transaction.transaction.id} - - ); -} - -export function StickyErrorProperties({ error, transaction }: Props) { - const isHandled = idx(error, _ => _.error.exception[0].handled); - const isRumAgent = isRumAgentName(error.agent.name); - - const { urlFieldName, urlValue } = isRumAgent - ? { - urlFieldName: ERROR_PAGE_URL, - urlValue: idx(error, _ => _.error.page.url) - } - : { - urlFieldName: URL_FULL, - urlValue: idx(error, _ => _.url.full) - }; - - const stickyProperties = [ - { - fieldName: '@timestamp', - label: i18n.translate('xpack.apm.errorGroupDetails.timestampLabel', { - defaultMessage: 'Timestamp' - }), - val: error['@timestamp'], - width: '50%' - }, - { - fieldName: urlFieldName, - label: 'URL', - val: urlValue || NOT_AVAILABLE_LABEL, - truncated: true, - width: '50%' - }, - { - fieldName: HTTP_REQUEST_METHOD, - label: i18n.translate('xpack.apm.errorGroupDetails.requestMethodLabel', { - defaultMessage: 'Request method' - }), - val: idx(error, _ => _.http.request.method) || NOT_AVAILABLE_LABEL, - width: '25%' - }, - { - fieldName: ERROR_EXC_HANDLED, - label: i18n.translate('xpack.apm.errorGroupDetails.handledLabel', { - defaultMessage: 'Handled' - }), - val: isBoolean(isHandled) ? String(isHandled) : NOT_AVAILABLE_LABEL, - width: '25%' - }, - { - fieldName: TRANSACTION_ID, - label: i18n.translate( - 'xpack.apm.errorGroupDetails.transactionSampleIdLabel', - { - defaultMessage: 'Transaction sample ID' - } - ), - val: , - width: '25%' - }, - { - fieldName: USER_ID, - label: i18n.translate('xpack.apm.errorGroupDetails.userIdLabel', { - defaultMessage: 'User ID' - }), - val: idx(error, _ => _.user.id) || NOT_AVAILABLE_LABEL, - width: '25%' - } - ]; - - return ; -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/StickyErrorProperties.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/StickyErrorProperties.test.tsx.snap deleted file mode 100644 index 74d0880d40fb..000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/StickyErrorProperties.test.tsx.snap +++ /dev/null @@ -1,74 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`StickyErrorProperties should render StickyProperties 1`] = ` -, - "width": "25%", - }, - Object { - "fieldName": "user.id", - "label": "User ID", - "val": "myUserId", - "width": "25%", - }, - ] - } -/> -`; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap index 39b9e588ce00..2c011c136f00 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/__snapshots__/index.test.tsx.snap @@ -4,7 +4,6 @@ exports[`DetailView should render Discover button 1`] = ` diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx index e3ba548a0152..f263629a40f6 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.test.tsx @@ -34,7 +34,9 @@ describe('DetailView', () => { occurrencesCount: 10, transaction: undefined, error: { - '@timestamp': 'myTimestamp', + timestamp: { + us: 0 + }, http: { request: { method: 'GET' } }, url: { full: 'myUrl' }, service: { name: 'myService' }, @@ -56,10 +58,14 @@ describe('DetailView', () => { expect(wrapper).toMatchSnapshot(); }); - it('should render StickyProperties', () => { + it('should render a Summary', () => { const errorGroup = { occurrencesCount: 10, - error: {} as any, + error: { + timestamp: { + us: 0 + } + } as any, transaction: undefined }; const wrapper = shallow( @@ -68,7 +74,7 @@ describe('DetailView', () => { urlParams={{}} location={{} as Location} /> - ).find('StickyErrorProperties'); + ).find('Summary'); expect(wrapper.exists()).toBe(true); }); @@ -78,7 +84,9 @@ describe('DetailView', () => { occurrencesCount: 10, transaction: undefined, error: { - '@timestamp': 'myTimestamp', + timestamp: { + us: 0 + }, service: {}, user: {} } as any @@ -100,7 +108,9 @@ describe('DetailView', () => { occurrencesCount: 10, transaction: undefined, error: { - '@timestamp': 'myTimestamp', + timestamp: { + us: 0 + }, context: {} } as any }; diff --git a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx index 46f1ce1457d6..41ccb6a6c736 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx @@ -10,7 +10,9 @@ import { EuiSpacer, EuiTab, EuiTabs, - EuiTitle + EuiTitle, + EuiIcon, + EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Location } from 'history'; @@ -21,7 +23,7 @@ import { idx } from '@kbn/elastic-idx'; import { ErrorGroupAPIResponse } from '../../../../../server/lib/errors/get_error_group'; import { APMError } from '../../../../../typings/es_schemas/ui/APMError'; import { IUrlParams } from '../../../../context/UrlParamsContext/types'; -import { px, unit } from '../../../../style/variables'; +import { px, unit, units } from '../../../../style/variables'; import { DiscoverErrorLink } from '../../../shared/Links/DiscoverLinks/DiscoverErrorLink'; import { fromQuery, toQuery } from '../../../shared/Links/url_helpers'; import { history } from '../../../../utils/history'; @@ -33,7 +35,10 @@ import { getTabs, logStacktraceTab } from './ErrorTabs'; -import { StickyErrorProperties } from './StickyErrorProperties'; +import { Summary } from '../../../shared/Summary'; +import { TimestampSummaryItem } from '../../../shared/Summary/TimestampSummaryItem'; +import { HttpInfoSummaryItem } from '../../../shared/Summary/HttpInfoSummaryItem'; +import { TransactionDetailLink } from '../../../shared/Links/apm/TransactionDetailLink'; const HeaderContainer = styled.div` display: flex; @@ -42,6 +47,12 @@ const HeaderContainer = styled.div` margin-bottom: ${px(unit)}; `; +const TransactionLinkName = styled.div` + margin-left: ${px(units.half)}; + display: inline-block; + vertical-align: middle; +`; + interface Props { errorGroup: ErrorGroupAPIResponse; urlParams: IUrlParams; @@ -67,6 +78,12 @@ export function DetailView({ errorGroup, urlParams, location }: Props) { const tabs = getTabs(error); const currentTab = getCurrentTab(tabs, urlParams.detailTab); + const errorUrl = + idx(error, _ => _.error.page.url) || idx(error, _ => _.url.full); + + const method = idx(error, _ => _.http.request.method); + const status = idx(error, _ => _.http.response.status_code); + return ( @@ -94,7 +111,41 @@ export function DetailView({ errorGroup, urlParams, location }: Props) { - + , + errorUrl && method ? ( + + ) : null, + transaction && ( + + + + + {transaction.transaction.name} + + + + ) + ]} + /> diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfTrace.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx similarity index 51% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfTrace.tsx rename to x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx index bf05d882ea54..813192793b93 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfTrace.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/PercentOfParent.tsx @@ -9,28 +9,30 @@ import { i18n } from '@kbn/i18n'; import { EuiToolTip } from '@elastic/eui'; import { asPercent } from '../../../../utils/formatters'; -interface PercentOfTraceProps { +interface PercentOfParentProps { duration: number; totalDuration?: number; + parentType: 'trace' | 'transaction'; } -export function PercentOfTrace({ +export function PercentOfParent({ duration, - totalDuration -}: PercentOfTraceProps) { + totalDuration, + parentType +}: PercentOfParentProps) { totalDuration = totalDuration || duration; const isOver100 = duration > totalDuration; - const percentOfTrace = isOver100 + const percentOfParent = isOver100 ? '>100%' : asPercent(duration, totalDuration, ''); - const percentOfTraceText = i18n.translate( - 'xpack.apm.transactionDetails.percentOfTrace', - { - defaultMessage: '{value} of trace', - values: { value: percentOfTrace } - } - ); + const percentOfParentText = i18n.translate('xpack.apm.percentOfParent', { + defaultMessage: + '({value} of {parentType, select, transaction { transaction } trace {trace} })', + values: { value: percentOfParent, parentType } + }); + + const childType = parentType === 'trace' ? 'transaction' : 'span'; return ( <> @@ -40,14 +42,18 @@ export function PercentOfTrace({ 'xpack.apm.transactionDetails.percentOfTraceLabelExplanation', { defaultMessage: - 'The % of trace exceeds 100% because this transaction takes longer than the root transaction.' + 'The % of {parentType, select, transaction {transaction} trace {trace} } exceeds 100% because this {childType, select, span {span} transaction {transaction} } takes longer than the root transaction.', + values: { + parentType, + childType + } } )} > - <>{percentOfTraceText} + <>{percentOfParentText} ) : ( - `(${percentOfTraceText})` + percentOfParentText )} ); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/StickyTransactionProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/StickyTransactionProperties.tsx index a9fb18776d58..4a8796da6315 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/StickyTransactionProperties.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/StickyTransactionProperties.tsx @@ -25,7 +25,7 @@ import { import { ErrorCountBadge } from './ErrorCountBadge'; import { isRumAgentName } from '../../../../../common/agent_name'; import { fontSize } from '../../../../style/variables'; -import { PercentOfTrace } from './PercentOfTrace'; +import { PercentOfParent } from './PercentOfParent'; interface Props { transaction: Transaction; @@ -96,7 +96,13 @@ export function StickyTransactionProperties({ defaultMessage: '% of trace' } ), - val: , + val: ( + + ), width: '25%' }, { diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Duration.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Duration.tsx deleted file mode 100644 index a22344d9c8ea..000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Duration.tsx +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 React from 'react'; -import { i18n } from '@kbn/i18n'; -import { EuiToolTip, EuiText } from '@elastic/eui'; -import styled from 'styled-components'; -import { asTime } from '../../../../../utils/formatters'; -import { PercentOfTrace } from '../PercentOfTrace'; - -interface DurationProps { - duration: number; - totalDuration?: number; -} - -const Span = styled('span')` - white-space: nowrap; -`; - -export function Duration({ duration, totalDuration }: DurationProps) { - totalDuration = totalDuration || duration; - const label = i18n.translate('xpack.apm.transactionDetails.durationLabel', { - defaultMessage: 'Duration' - }); - - return ( - - - {asTime(duration)} - {' '} - - - ); -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Timestamp.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Timestamp.tsx deleted file mode 100644 index 0a3aa4383bac..000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Timestamp.tsx +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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 React from 'react'; -import moment from 'moment'; -import { EuiToolTip, EuiText } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; - -interface TimestampProps { - transactionTimestamp: string; -} - -export function Timestamp({ transactionTimestamp }: TimestampProps) { - const timestamp = moment(transactionTimestamp).format( - 'MMM Do YYYY HH:mm:ss.SSS' - ); - - const label = i18n.translate('xpack.apm.transactionDetails.timestampLabel', { - defaultMessage: 'Timestamp' - }); - - return ( - - {timestamp} - - ); -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/TraceSummary.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/TraceSummary.tsx deleted file mode 100644 index 6f4138d3fcb6..000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/TraceSummary.tsx +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 React from 'react'; -import { EuiText, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; -import { idx } from '@kbn/elastic-idx'; -import { i18n } from '@kbn/i18n'; -import styled from 'styled-components'; -import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction'; -import { isRumAgentName } from '../../../../../../common/agent_name'; -import { ErrorCountBadge } from '../ErrorCountBadge'; -import { Duration } from './Duration'; -import { Timestamp } from './Timestamp'; -import { HttpInfo } from './HttpInfo'; -import { Result } from './Result'; -import { px, units } from '../../../../../../public/style/variables'; - -// TODO: Light/Dark theme (@see https://github.com/elastic/kibana/issues/44840) -const theme = euiLightVars; - -const Item = styled(EuiFlexItem)` - flex-wrap: nowrap; - border-right: 1px solid ${theme.euiColorMediumShade}; - padding-right: ${px(units.half)}; - - &:last-child { - border-right: none; - padding-right: 0; - } -`; - -interface Props { - errorCount: number; - totalDuration?: number; - transaction: Transaction; -} - -export function TraceSummary({ - errorCount, - totalDuration, - transaction -}: Props) { - const result = idx(transaction, _ => _.transaction.result); - const isRumAgent = isRumAgentName(transaction.agent.name); - const url = isRumAgent - ? idx(transaction, _ => _.transaction.page.url) - : idx(transaction, _ => _.url.full); - - return ( - <> - - - - - - - - - {url && ( - - - - )} - {result && !url && ( - - - - )} - {errorCount > 0 && ( - - - - {i18n.translate('xpack.apm.transactionDetails.errorCount', { - defaultMessage: - '{errorCount, number} {errorCount, plural, one {Error} other {Errors}}', - values: { errorCount } - })} - - - - )} - - - - ); -} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/index.ts b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/index.ts deleted file mode 100644 index e344d62c8a9f..000000000000 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/* - * 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. - */ -export { TraceSummary } from './TraceSummary'; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx index d463a96685b2..a49a557127be 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/FlyoutTopLevelProperties.tsx @@ -35,7 +35,7 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { {transaction.service.name} ), - width: '50%' + width: '25%' }, { label: i18n.translate('xpack.apm.transactionDetails.transactionLabel', { @@ -53,7 +53,7 @@ export function FlyoutTopLevelProperties({ transaction }: Props) { {transaction.transaction.name} ), - width: '50%' + width: '25%' } ]; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx index fd2f5bb3d606..12cd9fe86c8c 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/StickySpanProperties.tsx @@ -6,68 +6,63 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; +import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction'; import { - SPAN_ACTION, - SPAN_DURATION, SPAN_NAME, - SPAN_SUBTYPE, - SPAN_TYPE + TRANSACTION_NAME, + SERVICE_NAME } from '../../../../../../../../common/elasticsearch_fieldnames'; import { NOT_AVAILABLE_LABEL } from '../../../../../../../../common/i18n'; import { Span } from '../../../../../../../../typings/es_schemas/ui/Span'; -import { asMillis, asPercent } from '../../../../../../../utils/formatters'; import { StickyProperties } from '../../../../../../shared/StickyProperties'; - -function formatType(type: string) { - switch (type) { - case 'db': - return 'DB'; - case 'hard-navigation': - return i18n.translate( - 'xpack.apm.transactionDetails.spanFlyout.spanType.navigationTimingLabel', - { - defaultMessage: 'Navigation timing' - } - ); - default: - return type; - } -} - -function formatSubtype(subtype: string) { - switch (subtype) { - case 'mysql': - return 'MySQL'; - default: - return subtype; - } -} - -function getSpanTypes(span: Span) { - const { type, subtype, action } = span.span; - - const [primaryType, subtypeFromType, actionFromType] = type.split('.'); // This is to support 6.x data - - return { - spanType: formatType(primaryType), - spanSubtype: formatSubtype(subtype || subtypeFromType), - spanAction: action || actionFromType - }; -} +import { TransactionOverviewLink } from '../../../../../../shared/Links/apm/TransactionOverviewLink'; +import { TransactionDetailLink } from '../../../../../../shared/Links/apm/TransactionDetailLink'; interface Props { span: Span; - totalDuration?: number; + transaction?: Transaction; } -export function StickySpanProperties({ span, totalDuration }: Props) { - if (!totalDuration) { - return null; - } - +export function StickySpanProperties({ span, transaction }: Props) { const spanName = span.span.name; - const spanDuration = span.span.duration.us; - const { spanType, spanSubtype, spanAction } = getSpanTypes(span); + const transactionStickyProperties = transaction + ? [ + { + label: i18n.translate('xpack.apm.transactionDetails.serviceLabel', { + defaultMessage: 'Service' + }), + fieldName: SERVICE_NAME, + val: ( + + {transaction.service.name} + + ), + width: '25%' + }, + { + label: i18n.translate( + 'xpack.apm.transactionDetails.transactionLabel', + { + defaultMessage: 'Transaction' + } + ), + fieldName: TRANSACTION_NAME, + val: ( + + {transaction.transaction.name} + + ), + width: '25%' + } + ] + : []; + const stickyProperties = [ { label: i18n.translate( @@ -79,72 +74,10 @@ export function StickySpanProperties({ span, totalDuration }: Props) { fieldName: SPAN_NAME, val: spanName || NOT_AVAILABLE_LABEL, truncated: true, - width: '50%' + width: '25%' }, - { - fieldName: SPAN_DURATION, - label: i18n.translate( - 'xpack.apm.transactionDetails.spanFlyout.durationLabel', - { - defaultMessage: 'Duration' - } - ), - val: asMillis(spanDuration), - width: '50%' - }, - { - label: i18n.translate( - 'xpack.apm.transactionDetails.spanFlyout.percentOfTransactionLabel', - { - defaultMessage: '% of transaction' - } - ), - val: asPercent(spanDuration, totalDuration), - width: '50%' - }, - { - fieldName: SPAN_TYPE, - label: i18n.translate( - 'xpack.apm.transactionDetails.spanFlyout.typeLabel', - { - defaultMessage: 'Type' - } - ), - val: spanType, - truncated: true, - width: '15%' - } + ...transactionStickyProperties ]; - if (spanSubtype) { - stickyProperties.push({ - fieldName: SPAN_SUBTYPE, - label: i18n.translate( - 'xpack.apm.transactionDetails.spanFlyout.subtypeLabel', - { - defaultMessage: 'Subtype' - } - ), - val: spanSubtype, - truncated: true, - width: '15%' - }); - } - - if (spanAction) { - stickyProperties.push({ - fieldName: SPAN_ACTION, - label: i18n.translate( - 'xpack.apm.transactionDetails.spanFlyout.actionLabel', - { - defaultMessage: 'Action' - } - ), - val: spanAction, - truncated: true, - width: '15%' - }); - } - return ; } diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx index a5e87420860e..e0519de87e04 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/SpanFlyout/index.tsx @@ -15,27 +15,72 @@ import { EuiPortal, EuiSpacer, EuiTabbedContent, - EuiTitle + EuiTitle, + EuiBadge, + EuiToolTip } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { get, keys } from 'lodash'; import React, { Fragment } from 'react'; import styled from 'styled-components'; import { idx } from '@kbn/elastic-idx'; +import { px, units } from '../../../../../../../style/variables'; +import { Summary } from '../../../../../../shared/Summary'; +import { TimestampSummaryItem } from '../../../../../../shared/Summary/TimestampSummaryItem'; +import { DurationSummaryItem } from '../../../../../../shared/Summary/DurationSummaryItem'; import { Span } from '../../../../../../../../typings/es_schemas/ui/Span'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction'; import { DiscoverSpanLink } from '../../../../../../shared/Links/DiscoverLinks/DiscoverSpanLink'; import { Stacktrace } from '../../../../../../shared/Stacktrace'; -import { FlyoutTopLevelProperties } from '../FlyoutTopLevelProperties'; import { ResponsiveFlyout } from '../ResponsiveFlyout'; import { DatabaseContext } from './DatabaseContext'; import { HttpContext } from './HttpContext'; import { StickySpanProperties } from './StickySpanProperties'; +function formatType(type: string) { + switch (type) { + case 'db': + return 'DB'; + case 'hard-navigation': + return i18n.translate( + 'xpack.apm.transactionDetails.spanFlyout.spanType.navigationTimingLabel', + { + defaultMessage: 'Navigation timing' + } + ); + default: + return type; + } +} + +function formatSubtype(subtype: string | undefined) { + switch (subtype) { + case 'mysql': + return 'MySQL'; + default: + return subtype; + } +} + +function getSpanTypes(span: Span) { + const { type, subtype, action } = span.span; + + return { + spanType: formatType(type), + spanSubtype: formatSubtype(subtype), + spanAction: action + }; +} + const TagName = styled.div` font-weight: bold; `; +const SpanBadge = styled(EuiBadge)` + display: inline-block; + margin-right: ${px(units.quarter)}; +`; + interface Props { span?: Span; parentTransaction?: Transaction; @@ -62,6 +107,7 @@ export function SpanFlyout({ key, value: get(spanLabels, key) })); + const spanTypes = getSpanTypes(span); return ( @@ -96,9 +142,50 @@ export function SpanFlyout({ - - - + + + , + , + <> + + {spanTypes.spanType} + + {spanTypes.spanSubtype && ( + + + {spanTypes.spanSubtype} + + + )} + {spanTypes.spanAction && ( + + {spanTypes.spanAction} + + )} + + ]} + /> diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx index a857b331065c..db3e43574023 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/WaterfallContainer/Waterfall/TransactionFlyout/index.tsx @@ -9,7 +9,6 @@ import { EuiFlexItem, EuiFlyoutBody, EuiFlyoutHeader, - EuiHorizontalRule, EuiPortal, EuiSpacer, EuiTitle @@ -18,7 +17,7 @@ import { i18n } from '@kbn/i18n'; import React from 'react'; import { Transaction } from '../../../../../../../../typings/es_schemas/ui/Transaction'; import { TransactionActionMenu } from '../../../../../../shared/TransactionActionMenu/TransactionActionMenu'; -import { StickyTransactionProperties } from '../../../StickyTransactionProperties'; +import { TransactionSummary } from '../../../../../../shared/Summary/TransactionSummary'; import { FlyoutTopLevelProperties } from '../FlyoutTopLevelProperties'; import { ResponsiveFlyout } from '../ResponsiveFlyout'; import { TransactionMetadata } from '../../../../../../shared/MetadataTable/TransactionMetadata'; @@ -82,13 +81,13 @@ export function TransactionFlyout({ - - + - + diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx index ad83a634bda2..e518751e4153 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/index.tsx @@ -25,7 +25,7 @@ import { TransactionActionMenu } from '../../../shared/TransactionActionMenu/Tra import { TransactionTabs } from './TransactionTabs'; import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers'; import { LoadingStatePrompt } from '../../../shared/LoadingStatePrompt'; -import { TraceSummary } from './TraceSummary/TraceSummary'; +import { TransactionSummary } from '../../../shared/Summary/TransactionSummary'; function MaybeViewTraceLink({ transaction, @@ -165,7 +165,7 @@ export const WaterfallWithSummmary: React.SFC = ({ - { + const calculatedTotalDuration = + totalDuration === undefined ? duration : totalDuration; + + const label = i18n.translate('xpack.apm.transactionDurationLabel', { + defaultMessage: 'Duration' + }); + return ( + <> + + {asTime(duration)} + +   + + + + + ); +}; + +export { DurationSummaryItem }; diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx new file mode 100644 index 000000000000..9f07c06f6ff5 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/ErrorCountSummaryItem.tsx @@ -0,0 +1,33 @@ +/* + * 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 React from 'react'; +import { i18n } from '@kbn/i18n'; +import styled from 'styled-components'; +import { px } from '../../../../../code/public/style/variables'; +import { ErrorCountBadge } from '../../app/TransactionDetails/WaterfallWithSummmary/ErrorCountBadge'; +import { units } from '../../../style/variables'; + +interface Props { + count: number; +} + +const Badge = styled(ErrorCountBadge)` + margin-top: ${px(units.eighth)}; +`; + +const ErrorCountSummaryItem = ({ count }: Props) => { + return ( + + {i18n.translate('xpack.apm.transactionDetails.errorCount', { + defaultMessage: + '{errorCount, number} {errorCount, plural, one {Error} other {Errors}}', + values: { errorCount: count } + })} + + ); +}; + +export { ErrorCountSummaryItem }; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/HttpInfo.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/HttpInfoSummaryItem.test.tsx similarity index 64% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/HttpInfo.test.tsx rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/HttpInfoSummaryItem.test.tsx index 26bd54909d32..e87c16b9fd10 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/HttpInfo.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/HttpInfoSummaryItem.test.tsx @@ -6,24 +6,26 @@ import React from 'react'; import { shallow, mount } from 'enzyme'; -import { HttpInfo } from './HttpInfo'; -import * as exampleTransactions from './__fixtures__/transactions'; import { palettes } from '@elastic/eui'; -import { cloneDeep, set } from 'lodash'; +import { HttpInfoSummaryItem } from './'; +import * as exampleTransactions from '../__fixtures__/transactions'; -describe('HttpInfo', () => { +describe('HttpInfoSummaryItem', () => { describe('render', () => { const transaction = exampleTransactions.httpOk; const url = 'https://example.com'; - const props = { transaction, url }; + const method = 'get'; + const props = { transaction, url, method, status: 200 }; it('renders', () => { - expect(() => shallow()).not.toThrowError(); + expect(() => + shallow() + ).not.toThrowError(); }); describe('with status code 200', () => { it('shows a success color', () => { - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual( palettes.euiPaletteForStatus.colors[0] @@ -33,10 +35,9 @@ describe('HttpInfo', () => { describe('with status code 301', () => { it('shows a warning color', () => { - const p = cloneDeep(props); - set(p, 'transaction.http.response.status_code', 301); + const p = { ...props, status: 301 }; - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual( palettes.euiPaletteForStatus.colors[4] @@ -46,10 +47,9 @@ describe('HttpInfo', () => { describe('with status code 404', () => { it('shows a error color', () => { - const p = cloneDeep(props); - set(p, 'transaction.http.response.status_code', 404); + const p = { ...props, status: 404 }; - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual( palettes.euiPaletteForStatus.colors[9] @@ -59,10 +59,9 @@ describe('HttpInfo', () => { describe('with status code 502', () => { it('shows a error color', () => { - const p = cloneDeep(props); - set(p, 'transaction.http.response.status_code', 502); + const p = { ...props, status: 502 }; - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual( palettes.euiPaletteForStatus.colors[9] @@ -72,10 +71,9 @@ describe('HttpInfo', () => { describe('with some other status code', () => { it('shows the default color', () => { - const p = cloneDeep(props); - set(p, 'transaction.http.response.status_code', 700); + const p = { ...props, status: 700 }; - const wrapper = mount(); + const wrapper = mount(); expect(wrapper.find('HttpStatusBadge EuiBadge').prop('color')).toEqual( 'default' diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/HttpInfo.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx similarity index 80% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/HttpInfo.tsx rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx index 477904115b0a..d433dae75a1a 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/HttpInfo.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/index.tsx @@ -8,10 +8,8 @@ import React from 'react'; import { EuiToolTip, EuiBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import styled from 'styled-components'; -import { idx } from '@kbn/elastic-idx/target'; import { palettes } from '@elastic/eui'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction'; -import { units, px, truncate, unit } from '../../../../../style/variables'; +import { units, px, truncate, unit } from '../../../../style/variables'; import { statusCodes } from './statusCodes'; const statusColors = { @@ -58,7 +56,8 @@ const Url = styled('span')` ${truncate(px(unit * 24))}; `; interface HttpInfoProps { - transaction: Transaction; + method: string; + status?: number; url: string; } @@ -66,12 +65,7 @@ const Span = styled('span')` white-space: nowrap; `; -export function HttpInfo({ transaction, url }: HttpInfoProps) { - const method = ( - idx(transaction, _ => _.http.request.method) || '' - ).toUpperCase(); - const status = idx(transaction, _ => _.http.response.status_code); - +export function HttpInfoSummaryItem({ status, method, url }: HttpInfoProps) { const methodLabel = i18n.translate( 'xpack.apm.transactionDetails.requestMethodLabel', { @@ -83,7 +77,7 @@ export function HttpInfo({ transaction, url }: HttpInfoProps) { - {method} + <>{method.toUpperCase()} {' '} {url} diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/statusCodes.ts b/x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/statusCodes.ts similarity index 100% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/statusCodes.ts rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/HttpInfoSummaryItem/statusCodes.ts diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TimestampSummaryItem.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TimestampSummaryItem.tsx new file mode 100644 index 000000000000..8d619d94067c --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TimestampSummaryItem.tsx @@ -0,0 +1,26 @@ +/* + * 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 React from 'react'; +import { EuiToolTip } from '@elastic/eui'; +import moment from 'moment-timezone'; + +interface Props { + time: number; +} + +const TimestampSummaryItem = (props: Props) => { + const time = moment.tz(props.time, moment.tz.guess()); + const relativeTimeLabel = time.fromNow(); + const absoluteTimeLabel = time.format('MMM Do YYYY HH:mm:ss.SSS zz'); + + return ( + + <>{relativeTimeLabel} + + ); +}; + +export { TimestampSummaryItem }; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Result.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx similarity index 79% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Result.tsx rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx index 635282145e66..34a78982f7f2 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/Result.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionResultSummaryItem.tsx @@ -8,11 +8,11 @@ import React from 'react'; import { EuiToolTip, EuiBadge } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -interface ResultProps { - result: string; +interface Props { + transactionResult: string; } -export function Result({ result }: ResultProps) { +export function TransactionResultSummaryItem({ transactionResult }: Props) { return ( - {result} + {transactionResult} ); diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/TraceSummary.test.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx similarity index 68% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/TraceSummary.test.tsx rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx index 3a2d0e299a05..19f626187cdd 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/TraceSummary.test.tsx +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.test.tsx @@ -5,12 +5,12 @@ */ import { shallow } from 'enzyme'; -import { TraceSummary } from '.'; import React from 'react'; -import { Transaction } from '../../../../../../typings/es_schemas/ui/Transaction'; +import { TransactionSummary } from './TransactionSummary'; +import { Transaction } from '../../../../typings/es_schemas/ui/Transaction'; import * as exampleTransactions from './__fixtures__/transactions'; -describe('TraceSummary', () => { +describe('TransactionSummary', () => { describe('render', () => { const transaction: Transaction = exampleTransactions.httpOk; @@ -21,7 +21,9 @@ describe('TraceSummary', () => { }; it('renders', () => { - expect(() => shallow()).not.toThrowError(); + expect(() => + shallow() + ).not.toThrowError(); }); }); }); diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx new file mode 100644 index 000000000000..aea7e3ef2d38 --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/TransactionSummary.tsx @@ -0,0 +1,63 @@ +/* + * 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 React from 'react'; +import { idx } from '@kbn/elastic-idx'; +import { Transaction } from '../../../../typings/es_schemas/ui/Transaction'; +import { Summary } from './'; +import { TimestampSummaryItem } from './TimestampSummaryItem'; +import { DurationSummaryItem } from './DurationSummaryItem'; +import { ErrorCountSummaryItem } from './ErrorCountSummaryItem'; +import { isRumAgentName } from '../../../../common/agent_name'; +import { HttpInfoSummaryItem } from './HttpInfoSummaryItem'; +import { TransactionResultSummaryItem } from './TransactionResultSummaryItem'; + +interface Props { + transaction: Transaction; + totalDuration: number | undefined; + errorCount: number; +} + +const getTransactionResultSummaryItem = (transaction: Transaction) => { + const result = idx(transaction, _ => _.transaction.result); + const isRumAgent = isRumAgentName(transaction.agent.name); + const url = isRumAgent + ? idx(transaction, _ => _.transaction.page.url) + : idx(transaction, _ => _.url.full); + + if (url) { + const method = idx(transaction, _ => _.http.request.method) || ''; + const status = idx(transaction, _ => _.http.response.status_code); + + return ; + } + + if (result) { + return ; + } + + return null; +}; + +const TransactionSummary = ({ + transaction, + totalDuration, + errorCount +}: Props) => { + const items = [ + , + , + getTransactionResultSummaryItem(transaction), + errorCount ? : null + ]; + + return ; +}; + +export { TransactionSummary }; diff --git a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/__fixtures__/transactions.ts b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts similarity index 89% rename from x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/__fixtures__/transactions.ts rename to x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts index 06f36dfa18ff..59496dad1657 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/TransactionDetails/WaterfallWithSummmary/TraceSummary/__fixtures__/transactions.ts +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/__fixtures__/transactions.ts @@ -4,7 +4,7 @@ * you may not use this file except in compliance with the Elastic License. */ -import { Transaction } from '../../../../../../../typings/es_schemas/ui/Transaction'; +import { Transaction } from '../../../../../typings/es_schemas/ui/Transaction'; export const httpOk: Transaction = { '@timestamp': '0', diff --git a/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx b/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx new file mode 100644 index 000000000000..c4b750a360ef --- /dev/null +++ b/x-pack/legacy/plugins/apm/public/components/shared/Summary/index.tsx @@ -0,0 +1,46 @@ +/* + * 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 React from 'react'; +import { EuiFlexGrid, EuiFlexItem } from '@elastic/eui'; +import styled from 'styled-components'; +import euiLightVars from '@elastic/eui/dist/eui_theme_light.json'; +import { px, units } from '../../../../public/style/variables'; + +interface Props { + items: Array; +} + +// TODO: Light/Dark theme (@see https://github.com/elastic/kibana/issues/44840) +const theme = euiLightVars; + +const Item = styled(EuiFlexItem)` + flex-wrap: nowrap; + border-right: 1px solid ${theme.euiColorLightShade}; + padding-right: ${px(units.half)}; + flex-flow: row nowrap; + line-height: 1.5; + align-items: center !important; + &:last-child { + border-right: none; + padding-right: 0; + } +`; + +const Summary = ({ items }: Props) => { + const filteredItems = items.filter(Boolean) as React.ReactElement[]; + + return ( + + {filteredItems.map((item, index) => ( + + {item} + + ))} + + ); +}; + +export { Summary }; diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 2d95899c7099..bdc53e494ec0 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3315,17 +3315,12 @@ "xpack.apm.errorGroupDetails.errorGroupTitle": "エラーグループ {errorGroupId}", "xpack.apm.errorGroupDetails.errorOccurrenceTitle": "エラーのオカレンス", "xpack.apm.errorGroupDetails.exceptionMessageLabel": "例外メッセージ", - "xpack.apm.errorGroupDetails.handledLabel": "対応済み", "xpack.apm.errorGroupDetails.logMessageLabel": "ログメッセージ", "xpack.apm.errorGroupDetails.noErrorsLabel": "エラーが見つかりませんでした", "xpack.apm.errorGroupDetails.occurrencesChartLabel": "オカレンス", "xpack.apm.errorGroupDetails.occurrencesLongLabel": "{occCount} 件", "xpack.apm.errorGroupDetails.occurrencesShortLabel": "{occCount} 件", - "xpack.apm.errorGroupDetails.requestMethodLabel": "リクエストメソド", - "xpack.apm.errorGroupDetails.timestampLabel": "タイムスタンプ", - "xpack.apm.errorGroupDetails.transactionSampleIdLabel": "トランザクションサンプル ID", "xpack.apm.errorGroupDetails.unhandledLabel": "未対応", - "xpack.apm.errorGroupDetails.userIdLabel": "ユーザー ID", "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "ディスカバリで {occurrencesCount} 件のオカレンスを表示", "xpack.apm.errorsTable.errorMessageAndCulpritColumnLabel": "エラーメッセージと原因", "xpack.apm.errorsTable.groupIdColumnLabel": "グループ ID", @@ -3494,16 +3489,11 @@ "xpack.apm.transactionDetails.resultLabel": "結果", "xpack.apm.transactionDetails.serviceLabel": "サービス", "xpack.apm.transactionDetails.servicesTitle": "サービス", - "xpack.apm.transactionDetails.spanFlyout.actionLabel": "アクション", "xpack.apm.transactionDetails.spanFlyout.databaseStatementTitle": "データベースステートメント", - "xpack.apm.transactionDetails.spanFlyout.durationLabel": "期間", "xpack.apm.transactionDetails.spanFlyout.nameLabel": "名前", - "xpack.apm.transactionDetails.spanFlyout.percentOfTransactionLabel": "トランザクションの %", "xpack.apm.transactionDetails.spanFlyout.spanDetailsTitle": "スパン詳細", "xpack.apm.transactionDetails.spanFlyout.spanType.navigationTimingLabel": "ナビゲーションタイミング", "xpack.apm.transactionDetails.spanFlyout.stackTraceTabLabel": "スタックトレース", - "xpack.apm.transactionDetails.spanFlyout.subtypeLabel": "サブタイプ", - "xpack.apm.transactionDetails.spanFlyout.typeLabel": "タイプ", "xpack.apm.transactionDetails.spanFlyout.viewSpanInDiscoverButtonLabel": "ディスカバリでスパンを表示", "xpack.apm.transactionDetails.timestampLabel": "タイムスタンプ", "xpack.apm.transactionDetails.transactionLabel": "トランザクション", @@ -11572,4 +11562,4 @@ "xpack.fileUpload.fileParser.noFileProvided": "エラー、ファイルが提供されていません", "xpack.fileUpload.jsonIndexFilePicker.errorGettingIndexName": "インデックス名の取得中にエラーが発生: {errorMessage}" } -} \ No newline at end of file +} diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 2214faea0725..2bde77133f75 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3316,17 +3316,12 @@ "xpack.apm.errorGroupDetails.errorGroupTitle": "错误组 {errorGroupId}", "xpack.apm.errorGroupDetails.errorOccurrenceTitle": "错误发生", "xpack.apm.errorGroupDetails.exceptionMessageLabel": "异常消息", - "xpack.apm.errorGroupDetails.handledLabel": "已处理", "xpack.apm.errorGroupDetails.logMessageLabel": "日志消息", "xpack.apm.errorGroupDetails.noErrorsLabel": "未找到任何错误", "xpack.apm.errorGroupDetails.occurrencesChartLabel": "发生次数", "xpack.apm.errorGroupDetails.occurrencesLongLabel": "{occCount} 次发生", "xpack.apm.errorGroupDetails.occurrencesShortLabel": "{occCount} 次发生", - "xpack.apm.errorGroupDetails.requestMethodLabel": "请求方法", - "xpack.apm.errorGroupDetails.timestampLabel": "时间戳", - "xpack.apm.errorGroupDetails.transactionSampleIdLabel": "事务样例 ID", "xpack.apm.errorGroupDetails.unhandledLabel": "未处理", - "xpack.apm.errorGroupDetails.userIdLabel": "用户 ID", "xpack.apm.errorGroupDetails.viewOccurrencesInDiscoverButtonLabel": "在 Discover 中查看 {occurrencesCount} 次发生", "xpack.apm.errorsTable.errorMessageAndCulpritColumnLabel": "错误消息和原因", "xpack.apm.errorsTable.groupIdColumnLabel": "组 ID", @@ -3495,16 +3490,11 @@ "xpack.apm.transactionDetails.resultLabel": "结果", "xpack.apm.transactionDetails.serviceLabel": "服务", "xpack.apm.transactionDetails.servicesTitle": "服务", - "xpack.apm.transactionDetails.spanFlyout.actionLabel": "操作", "xpack.apm.transactionDetails.spanFlyout.databaseStatementTitle": "数据库语句", - "xpack.apm.transactionDetails.spanFlyout.durationLabel": "持续时间", "xpack.apm.transactionDetails.spanFlyout.nameLabel": "名称", - "xpack.apm.transactionDetails.spanFlyout.percentOfTransactionLabel": "事务的 %", "xpack.apm.transactionDetails.spanFlyout.spanDetailsTitle": "跨度详情", "xpack.apm.transactionDetails.spanFlyout.spanType.navigationTimingLabel": "导航定时", "xpack.apm.transactionDetails.spanFlyout.stackTraceTabLabel": "堆栈追溯", - "xpack.apm.transactionDetails.spanFlyout.subtypeLabel": "子类型", - "xpack.apm.transactionDetails.spanFlyout.typeLabel": "类型", "xpack.apm.transactionDetails.spanFlyout.viewSpanInDiscoverButtonLabel": "在 Discover 中查看跨度", "xpack.apm.transactionDetails.timestampLabel": "时间戳", "xpack.apm.transactionDetails.transactionLabel": "事务", @@ -11574,4 +11564,4 @@ "xpack.fileUpload.fileParser.noFileProvided": "错误,未提供任何文件", "xpack.fileUpload.jsonIndexFilePicker.errorGettingIndexName": "检索索引名称时出错:{errorMessage}" } -} \ No newline at end of file +}