diff --git a/static/app/views/insights/browser/resources/components/charts/resourceLandingPageCharts.tsx b/static/app/views/insights/browser/resources/components/charts/resourceLandingPageCharts.tsx index 437901eff9a0fc..cb186b085e06d2 100644 --- a/static/app/views/insights/browser/resources/components/charts/resourceLandingPageCharts.tsx +++ b/static/app/views/insights/browser/resources/components/charts/resourceLandingPageCharts.tsx @@ -1,83 +1,23 @@ import styled from '@emotion/styled'; import {space} from 'sentry/styles/space'; -import {EMPTY_OPTION_VALUE, MutableSearch} from 'sentry/utils/tokenizeSearch'; -import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget'; -import {useSpanMetricsSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries'; -import type {ModuleFilters} from 'sentry/views/insights/common/views/spans/types'; -import { - getDurationChartTitle, - getThroughputChartTitle, -} from 'sentry/views/insights/common/views/spans/types'; -import {SpanMetricsField} from 'sentry/views/insights/types'; - -const {SPAN_SELF_TIME, NORMALIZED_DESCRIPTION, SPAN_DOMAIN} = SpanMetricsField; - -type Props = { - appliedFilters: ModuleFilters; - extraQuery?: string[]; -}; - -export function ResourceLandingPageCharts({appliedFilters, extraQuery}: Props) { - let query: string = buildDiscoverQueryConditions(appliedFilters); - - if (extraQuery) { - query += ` ${extraQuery.join(' ')}`; - } - - const {data, isPending, error} = useSpanMetricsSeries( - { - search: new MutableSearch(query), - yAxis: ['epm()', `avg(${SPAN_SELF_TIME})`], - transformAliasToInputFormat: true, - }, - 'api.starfish.span-time-charts' - ); +import {ResourceLandingDurationChartWidget} from 'sentry/views/insights/common/components/widgets/resourceLandingDurationChartWidget'; +import {ResourceLandingThroughputChartWidget} from 'sentry/views/insights/common/components/widgets/resourceLandingThroughputChartWidget'; +export function ResourceLandingPageCharts() { return ( - + - + ); } -const SPAN_FILTER_KEYS = ['span_operation', SPAN_DOMAIN, 'action']; - -const buildDiscoverQueryConditions = (appliedFilters: ModuleFilters) => { - const result = Object.keys(appliedFilters) - .filter(key => SPAN_FILTER_KEYS.includes(key)) - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - .filter(key => Boolean(appliedFilters[key])) - .map(key => { - // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message - const value = appliedFilters[key]; - if (key === SPAN_DOMAIN && value === EMPTY_OPTION_VALUE) { - return [`!has:${SPAN_DOMAIN}`]; - } - return `${key}:${value}`; - }); - - result.push(`has:${NORMALIZED_DESCRIPTION}`); - - return result.join(' '); -}; - const ChartsContainer = styled('div')` display: flex; flex-direction: row; diff --git a/static/app/views/insights/browser/resources/components/charts/resourceSummaryCharts.tsx b/static/app/views/insights/browser/resources/components/charts/resourceSummaryCharts.tsx index 18726cb67f0db9..8ca6b7a8a04a7d 100644 --- a/static/app/views/insights/browser/resources/components/charts/resourceSummaryCharts.tsx +++ b/static/app/views/insights/browser/resources/components/charts/resourceSummaryCharts.tsx @@ -1,105 +1,23 @@ import {Fragment} from 'react'; -import {t} from 'sentry/locale'; -import {MutableSearch} from 'sentry/utils/tokenizeSearch'; -import {Referrer} from 'sentry/views/insights/browser/resources/referrer'; -import {DATA_TYPE, FIELD_ALIASES} from 'sentry/views/insights/browser/resources/settings'; -import {useResourceModuleFilters} from 'sentry/views/insights/browser/resources/utils/useResourceFilters'; -import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget'; import * as ModuleLayout from 'sentry/views/insights/common/components/moduleLayout'; -import {useSpanMetricsSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries'; -import { - getDurationChartTitle, - getThroughputChartTitle, -} from 'sentry/views/insights/common/views/spans/types'; -import {SpanMetricsField} from 'sentry/views/insights/types'; - -const { - SPAN_SELF_TIME, - HTTP_RESPONSE_CONTENT_LENGTH, - HTTP_DECODED_RESPONSE_CONTENT_LENGTH, - HTTP_RESPONSE_TRANSFER_SIZE, - RESOURCE_RENDER_BLOCKING_STATUS, -} = SpanMetricsField; - -function ResourceSummaryCharts(props: {groupId: string}) { - const filters = useResourceModuleFilters(); - - const mutableSearch = MutableSearch.fromQueryObject({ - 'span.group': props.groupId, - ...(filters[RESOURCE_RENDER_BLOCKING_STATUS] - ? { - [RESOURCE_RENDER_BLOCKING_STATUS]: filters[RESOURCE_RENDER_BLOCKING_STATUS], - } - : {}), - ...(filters[SpanMetricsField.USER_GEO_SUBREGION] - ? { - [SpanMetricsField.USER_GEO_SUBREGION]: `[${filters[SpanMetricsField.USER_GEO_SUBREGION].join(',')}]`, - } - : {}), - }); - - const { - data: spanMetricsSeriesData, - isPending: areSpanMetricsSeriesLoading, - error: spanMetricsSeriesError, - } = useSpanMetricsSeries( - { - search: mutableSearch, - yAxis: [ - `epm()`, - `avg(${SPAN_SELF_TIME})`, - `avg(${HTTP_RESPONSE_CONTENT_LENGTH})`, - `avg(${HTTP_DECODED_RESPONSE_CONTENT_LENGTH})`, - `avg(${HTTP_RESPONSE_TRANSFER_SIZE})`, - ], - enabled: Boolean(props.groupId), - transformAliasToInputFormat: true, - }, - Referrer.RESOURCE_SUMMARY_CHARTS - ); - - if (spanMetricsSeriesData) { - spanMetricsSeriesData[`avg(${HTTP_RESPONSE_TRANSFER_SIZE})`].lineStyle = { - type: 'dashed', - }; - spanMetricsSeriesData[`avg(${HTTP_DECODED_RESPONSE_CONTENT_LENGTH})`].lineStyle = { - type: 'dashed', - }; - } +import {ResourceSummaryAverageSizeChartWidget} from 'sentry/views/insights/common/components/widgets/resourceSummaryAverageSizeChartWidget'; +import {ResourceSummaryDurationChartWidget} from 'sentry/views/insights/common/components/widgets/resourceSummaryDurationChartWidget'; +import {ResourceSummaryThroughputChartWidget} from 'sentry/views/insights/common/components/widgets/resourceSummaryThroughputChartWidget'; +function ResourceSummaryCharts() { return ( - + - + - + ); diff --git a/static/app/views/insights/browser/resources/components/resourceView.tsx b/static/app/views/insights/browser/resources/components/resourceView.tsx index ddec211538d010..0abf456916a7bc 100644 --- a/static/app/views/insights/browser/resources/components/resourceView.tsx +++ b/static/app/views/insights/browser/resources/components/resourceView.tsx @@ -8,7 +8,6 @@ import {trackAnalytics} from 'sentry/utils/analytics'; import {useLocation} from 'sentry/utils/useLocation'; import {useNavigate} from 'sentry/utils/useNavigate'; import useOrganization from 'sentry/utils/useOrganization'; -import {getResourceTypeFilter} from 'sentry/views/insights/browser/common/queries/useResourcesQuery'; import RenderBlockingSelector from 'sentry/views/insights/browser/resources/components/renderBlockingSelector'; import ResourceTable from 'sentry/views/insights/browser/resources/components/tables/resourceTable'; import { @@ -24,16 +23,13 @@ import { import {useResourceSort} from 'sentry/views/insights/browser/resources/utils/useResourceSort'; import {QueryParameterNames} from 'sentry/views/insights/common/views/queryParameters'; import {TransactionSelector} from 'sentry/views/insights/common/views/spans/selectors/transactionSelector'; -import type {ModuleFilters} from 'sentry/views/insights/common/views/spans/types'; import {ResourceLandingPageCharts} from './charts/resourceLandingPageCharts'; const { SPAN_OP: RESOURCE_TYPE, - SPAN_DOMAIN, TRANSACTION, RESOURCE_RENDER_BLOCKING_STATUS, - USER_GEO_SUBREGION, } = BrowserStarfishFields; type Option = { @@ -45,25 +41,10 @@ function ResourceView() { const filters = useResourceModuleFilters(); const sort = useResourceSort(); - const spanTimeChartsFilters: ModuleFilters = { - 'span.op': `[${DEFAULT_RESOURCE_TYPES.join(',')}]`, - ...(filters[SPAN_DOMAIN] ? {[SPAN_DOMAIN]: filters[SPAN_DOMAIN]} : {}), - }; - - const extraQuery = [ - ...getResourceTypeFilter(undefined, DEFAULT_RESOURCE_TYPES), - ...(filters[USER_GEO_SUBREGION] - ? [`user.geo.subregion:[${filters[USER_GEO_SUBREGION].join(',')}]`] - : []), - ]; - return ( - + diff --git a/static/app/views/insights/browser/resources/views/resourceSummaryPage.tsx b/static/app/views/insights/browser/resources/views/resourceSummaryPage.tsx index 921d252fb2cf42..d8390dc864510e 100644 --- a/static/app/views/insights/browser/resources/views/resourceSummaryPage.tsx +++ b/static/app/views/insights/browser/resources/views/resourceSummaryPage.tsx @@ -155,7 +155,7 @@ function ResourceSummary() { )} - + diff --git a/static/app/views/insights/common/components/widgets/hooks/useResourceLandingSeries.tsx b/static/app/views/insights/common/components/widgets/hooks/useResourceLandingSeries.tsx new file mode 100644 index 00000000000000..7392b688d115a5 --- /dev/null +++ b/static/app/views/insights/common/components/widgets/hooks/useResourceLandingSeries.tsx @@ -0,0 +1,68 @@ +import type {PageFilters} from 'sentry/types/core'; +import {EMPTY_OPTION_VALUE, MutableSearch} from 'sentry/utils/tokenizeSearch'; +import {getResourceTypeFilter} from 'sentry/views/insights/browser/common/queries/useResourcesQuery'; +import {DEFAULT_RESOURCE_TYPES} from 'sentry/views/insights/browser/resources/settings'; +import {useResourceModuleFilters} from 'sentry/views/insights/browser/resources/utils/useResourceFilters'; +import {useSpanMetricsSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries'; +import type {ModuleFilters} from 'sentry/views/insights/common/views/spans/types'; +import {SpanMetricsField} from 'sentry/views/insights/types'; + +const {NORMALIZED_DESCRIPTION, SPAN_DOMAIN, SPAN_SELF_TIME, USER_GEO_SUBREGION} = + SpanMetricsField; + +const SPAN_FILTER_KEYS = ['span_operation', SPAN_DOMAIN, 'action']; + +const buildDiscoverQueryConditions = (appliedFilters: ModuleFilters) => { + const result = Object.keys(appliedFilters) + .filter(key => SPAN_FILTER_KEYS.includes(key)) + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + .filter(key => Boolean(appliedFilters[key])) + .map(key => { + // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message + const value = appliedFilters[key]; + if (key === SPAN_DOMAIN && value === EMPTY_OPTION_VALUE) { + return [`!has:${SPAN_DOMAIN}`]; + } + return `${key}:${value}`; + }); + + result.push(`has:${NORMALIZED_DESCRIPTION}`); + + return result.join(' '); +}; + +interface Props { + pageFilters?: PageFilters; +} + +export function useResourceLandingSeries(props: Props = {}) { + const filters = useResourceModuleFilters(); + + const spanTimeChartsFilters: ModuleFilters = { + 'span.op': `[${DEFAULT_RESOURCE_TYPES.join(',')}]`, + ...(filters[SPAN_DOMAIN] ? {[SPAN_DOMAIN]: filters[SPAN_DOMAIN]} : {}), + }; + + const extraQuery = [ + ...getResourceTypeFilter(undefined, DEFAULT_RESOURCE_TYPES), + ...(filters[USER_GEO_SUBREGION] + ? [`user.geo.subregion:[${filters[USER_GEO_SUBREGION].join(',')}]`] + : []), + ]; + + let query: string = buildDiscoverQueryConditions(spanTimeChartsFilters); + + if (extraQuery.length) { + query += ` ${extraQuery.join(' ')}`; + } + + return useSpanMetricsSeries( + { + search: new MutableSearch(query), + yAxis: ['epm()', `avg(${SPAN_SELF_TIME})`], + transformAliasToInputFormat: true, + }, + 'api.starfish.span-time-charts', + props.pageFilters + ); +} diff --git a/static/app/views/insights/common/components/widgets/hooks/useResourceSummarySeries.tsx b/static/app/views/insights/common/components/widgets/hooks/useResourceSummarySeries.tsx new file mode 100644 index 00000000000000..f8ad5a17334e6f --- /dev/null +++ b/static/app/views/insights/common/components/widgets/hooks/useResourceSummarySeries.tsx @@ -0,0 +1,54 @@ +import type {PageFilters} from 'sentry/types/core'; +import {MutableSearch} from 'sentry/utils/tokenizeSearch'; +import {Referrer} from 'sentry/views/insights/browser/resources/referrer'; +import {useResourceModuleFilters} from 'sentry/views/insights/browser/resources/utils/useResourceFilters'; +import {useSpanMetricsSeries} from 'sentry/views/insights/common/queries/useDiscoverSeries'; +import {SpanMetricsField} from 'sentry/views/insights/types'; + +const { + SPAN_SELF_TIME, + HTTP_RESPONSE_CONTENT_LENGTH, + HTTP_DECODED_RESPONSE_CONTENT_LENGTH, + HTTP_RESPONSE_TRANSFER_SIZE, + RESOURCE_RENDER_BLOCKING_STATUS, +} = SpanMetricsField; + +interface Props { + groupId?: string; + pageFilters?: PageFilters; +} + +export function useResourceSummarySeries({pageFilters, groupId}: Props = {}) { + const filters = useResourceModuleFilters(); + + const mutableSearch = MutableSearch.fromQueryObject({ + 'span.group': groupId, + ...(filters[RESOURCE_RENDER_BLOCKING_STATUS] + ? { + [RESOURCE_RENDER_BLOCKING_STATUS]: filters[RESOURCE_RENDER_BLOCKING_STATUS], + } + : {}), + ...(filters[SpanMetricsField.USER_GEO_SUBREGION] + ? { + [SpanMetricsField.USER_GEO_SUBREGION]: `[${filters[SpanMetricsField.USER_GEO_SUBREGION].join(',')}]`, + } + : {}), + }); + + return useSpanMetricsSeries( + { + search: mutableSearch, + yAxis: [ + `epm()`, + `avg(${SPAN_SELF_TIME})`, + `avg(${HTTP_RESPONSE_CONTENT_LENGTH})`, + `avg(${HTTP_DECODED_RESPONSE_CONTENT_LENGTH})`, + `avg(${HTTP_RESPONSE_TRANSFER_SIZE})`, + ], + enabled: Boolean(groupId), + transformAliasToInputFormat: true, + }, + Referrer.RESOURCE_SUMMARY_CHARTS, + pageFilters + ); +} diff --git a/static/app/views/insights/common/components/widgets/resourceLandingDurationChartWidget.tsx b/static/app/views/insights/common/components/widgets/resourceLandingDurationChartWidget.tsx new file mode 100644 index 00000000000000..9435c251386350 --- /dev/null +++ b/static/app/views/insights/common/components/widgets/resourceLandingDurationChartWidget.tsx @@ -0,0 +1,24 @@ +import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget'; +import {useResourceLandingSeries} from 'sentry/views/insights/common/components/widgets/hooks/useResourceLandingSeries'; +import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; +import {getDurationChartTitle} from 'sentry/views/insights/common/views/spans/types'; +import {SpanMetricsField} from 'sentry/views/insights/types'; + +const {SPAN_SELF_TIME} = SpanMetricsField; + +export function ResourceLandingDurationChartWidget(props: LoadableChartWidgetProps) { + const {data, isPending, error} = useResourceLandingSeries({ + pageFilters: props.pageFilters, + }); + + return ( + + ); +} diff --git a/static/app/views/insights/common/components/widgets/resourceLandingThroughputChartWidget.tsx b/static/app/views/insights/common/components/widgets/resourceLandingThroughputChartWidget.tsx new file mode 100644 index 00000000000000..861a0e906820a2 --- /dev/null +++ b/static/app/views/insights/common/components/widgets/resourceLandingThroughputChartWidget.tsx @@ -0,0 +1,21 @@ +import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget'; +import {useResourceLandingSeries} from 'sentry/views/insights/common/components/widgets/hooks/useResourceLandingSeries'; +import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; +import {getThroughputChartTitle} from 'sentry/views/insights/common/views/spans/types'; + +export function ResourceLandingThroughputChartWidget(props: LoadableChartWidgetProps) { + const {data, isPending, error} = useResourceLandingSeries({ + pageFilters: props.pageFilters, + }); + + return ( + + ); +} diff --git a/static/app/views/insights/common/components/widgets/resourceSummaryAverageSizeChartWidget.tsx b/static/app/views/insights/common/components/widgets/resourceSummaryAverageSizeChartWidget.tsx new file mode 100644 index 00000000000000..4162e78f081da3 --- /dev/null +++ b/static/app/views/insights/common/components/widgets/resourceSummaryAverageSizeChartWidget.tsx @@ -0,0 +1,46 @@ +import {t} from 'sentry/locale'; +import {useParams} from 'sentry/utils/useParams'; +import {DATA_TYPE, FIELD_ALIASES} from 'sentry/views/insights/browser/resources/settings'; +import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget'; +import {useResourceSummarySeries} from 'sentry/views/insights/common/components/widgets/hooks/useResourceSummarySeries'; +import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; +import {SpanMetricsField} from 'sentry/views/insights/types'; + +const { + HTTP_RESPONSE_CONTENT_LENGTH, + HTTP_DECODED_RESPONSE_CONTENT_LENGTH, + HTTP_RESPONSE_TRANSFER_SIZE, +} = SpanMetricsField; + +export function ResourceSummaryAverageSizeChartWidget(props: LoadableChartWidgetProps) { + const {groupId} = useParams(); + + const {data, isPending, error} = useResourceSummarySeries({ + groupId, + pageFilters: props.pageFilters, + }); + + if (data) { + data[`avg(${HTTP_RESPONSE_TRANSFER_SIZE})`].lineStyle = { + type: 'dashed', + }; + data[`avg(${HTTP_DECODED_RESPONSE_CONTENT_LENGTH})`].lineStyle = { + type: 'dashed', + }; + } + return ( + + ); +} diff --git a/static/app/views/insights/common/components/widgets/resourceSummaryDurationChartWidget.tsx b/static/app/views/insights/common/components/widgets/resourceSummaryDurationChartWidget.tsx new file mode 100644 index 00000000000000..ade1896b5ac4d5 --- /dev/null +++ b/static/app/views/insights/common/components/widgets/resourceSummaryDurationChartWidget.tsx @@ -0,0 +1,28 @@ +import {useParams} from 'sentry/utils/useParams'; +import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget'; +import {useResourceSummarySeries} from 'sentry/views/insights/common/components/widgets/hooks/useResourceSummarySeries'; +import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; +import {getDurationChartTitle} from 'sentry/views/insights/common/views/spans/types'; +import {SpanMetricsField} from 'sentry/views/insights/types'; + +const {SPAN_SELF_TIME} = SpanMetricsField; + +export function ResourceSummaryDurationChartWidget(props: LoadableChartWidgetProps) { + const {groupId} = useParams(); + + const {data, isPending, error} = useResourceSummarySeries({ + groupId, + pageFilters: props.pageFilters, + }); + + return ( + + ); +} diff --git a/static/app/views/insights/common/components/widgets/resourceSummaryThroughputChartWidget.tsx b/static/app/views/insights/common/components/widgets/resourceSummaryThroughputChartWidget.tsx new file mode 100644 index 00000000000000..ab49e3723eeca7 --- /dev/null +++ b/static/app/views/insights/common/components/widgets/resourceSummaryThroughputChartWidget.tsx @@ -0,0 +1,25 @@ +import {useParams} from 'sentry/utils/useParams'; +import {InsightsLineChartWidget} from 'sentry/views/insights/common/components/insightsLineChartWidget'; +import {useResourceSummarySeries} from 'sentry/views/insights/common/components/widgets/hooks/useResourceSummarySeries'; +import type {LoadableChartWidgetProps} from 'sentry/views/insights/common/components/widgets/types'; +import {getThroughputChartTitle} from 'sentry/views/insights/common/views/spans/types'; + +export function ResourceSummaryThroughputChartWidget(props: LoadableChartWidgetProps) { + const {groupId} = useParams(); + + const {data, isPending, error} = useResourceSummarySeries({ + groupId, + pageFilters: props.pageFilters, + }); + + return ( + + ); +}