123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- import {useMemo} from 'react';
- import type {Location} from 'history';
- import * as qs from 'query-string';
- import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
- import {DEFAULT_STATS_PERIOD} from 'sentry/constants';
- import type {PageFilters} from 'sentry/types/core';
- import type {TraceMeta} from 'sentry/utils/performance/quickTrace/types';
- import {type ApiQueryKey, useApiQueries} from 'sentry/utils/queryClient';
- import {decodeScalar} from 'sentry/utils/queryString';
- import useOrganization from 'sentry/utils/useOrganization';
- import usePageFilters from 'sentry/utils/usePageFilters';
- function getMetaQueryParams(
- query: Location['query'],
- filters: Partial<PageFilters> = {}
- ):
- | {
- // demo has the format ${projectSlug}:${eventId}
- // used to query a demo transaction event from the backend.
- demo: string | undefined;
- statsPeriod: string;
- }
- | {
- demo: string | undefined;
- timestamp: string;
- } {
- const normalizedParams = normalizeDateTimeParams(query, {
- allowAbsolutePageDatetime: true,
- });
- const statsPeriod = decodeScalar(normalizedParams.statsPeriod);
- const timestamp = decodeScalar(normalizedParams.timestamp);
- if (timestamp) {
- return {timestamp, demo: decodeScalar(normalizedParams.demo)};
- }
- return {
- statsPeriod: (statsPeriod || filters?.datetime?.period) ?? DEFAULT_STATS_PERIOD,
- demo: decodeScalar(normalizedParams.demo),
- };
- }
- function getTraceMetaQueryKey(
- traceSlug: string,
- orgSlug: string,
- queryParams:
- | {
- // demo has the format ${projectSlug}:${eventId}
- // used to query a demo transaction event from the backend.
- demo: string | undefined;
- statsPeriod: string;
- }
- | {
- demo: string | undefined;
- timestamp: string;
- }
- ): ApiQueryKey {
- return [
- `/organizations/${orgSlug}/events-trace-meta/${traceSlug}/`,
- {query: queryParams},
- ];
- }
- export type TraceMetaQueryResults = {
- data: TraceMeta;
- isLoading: boolean;
- isRefetching: boolean;
- refetch: () => void;
- };
- export function useTraceMeta(traceSlugs: string[]): {
- data: TraceMeta;
- isLoading: boolean;
- isRefetching: boolean;
- refetch: () => void;
- } {
- const filters = usePageFilters();
- const organization = useOrganization();
- const queryParams = useMemo(() => {
- const query = qs.parse(location.search);
- return getMetaQueryParams(query, filters.selection);
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
- const mode = queryParams.demo ? 'demo' : undefined;
- const queryKeys = useMemo(() => {
- return traceSlugs.map(traceSlug =>
- getTraceMetaQueryKey(traceSlug, organization.slug, queryParams)
- );
- }, [traceSlugs, organization, queryParams]);
- const results = useApiQueries<TraceMeta>(queryKeys, {
- enabled: traceSlugs.length > 0 && !!organization.slug && mode !== 'demo',
- staleTime: Infinity,
- });
- const mergedTraceMeta = useMemo(() => {
- const mergedResult: {
- data: TraceMeta;
- isLoading: boolean;
- isRefetching: boolean;
- refetch: () => void;
- } = {
- data: {
- errors: 0,
- performance_issues: 0,
- projects: 0,
- transactions: 0,
- },
- isLoading: false,
- isRefetching: false,
- refetch: () => {
- results.forEach(result => result.refetch());
- },
- };
- for (const metaResult of results) {
- mergedResult.isLoading ||= metaResult.isLoading;
- mergedResult.isRefetching ||= metaResult.isRefetching;
- mergedResult.data.errors += metaResult.data?.errors ?? 0;
- mergedResult.data.performance_issues += metaResult.data?.performance_issues ?? 0;
- // TODO: We want the count of unique projects, not the sum of all projects. When there are multiple traces, taking the best guess by using the max
- // project count accross all traces for now. We don't use the project count for anything yet, so this is fine for now.
- mergedResult.data.projects += Math.max(
- metaResult.data?.projects ?? 0,
- mergedResult.data.projects
- );
- mergedResult.data.transactions += metaResult.data?.transactions ?? 0;
- }
- return mergedResult;
- }, [results]);
- // When projects don't have performance set up, we allow them to view a sample transaction.
- // The backend creates the sample transaction, however the trace is created async, so when the
- // page loads, we cannot guarantee that querying the trace will succeed as it may not have been stored yet.
- // When this happens, we assemble a fake trace response to only include the transaction that had already been
- // created and stored already so that the users can visualize in the context of a trace.
- // The trace meta query has to reflect this by returning a single transaction and project.
- if (mode === 'demo') {
- return {
- data: {
- errors: 0,
- performance_issues: 0,
- projects: 1,
- transactions: 1,
- },
- isLoading: false,
- isRefetching: false,
- refetch: () => {},
- };
- }
- return mergedTraceMeta;
- }
|