|
@@ -1,106 +1,120 @@
|
|
|
import {Fragment} from 'react';
|
|
|
-import sumBy from 'lodash/sumBy';
|
|
|
|
|
|
+import {openHelpSearchModal} from 'sentry/actionCreators/modal';
|
|
|
+import {Button} from 'sentry/components/button';
|
|
|
import ExternalLink from 'sentry/components/links/externalLink';
|
|
|
import {t, tct} from 'sentry/locale';
|
|
|
+import {Project} from 'sentry/types';
|
|
|
import useOrganization from 'sentry/utils/useOrganization';
|
|
|
import usePageFilters from 'sentry/utils/usePageFilters';
|
|
|
import {normalizeUrl} from 'sentry/utils/withDomainRequired';
|
|
|
-import {useIneligibleProjects} from 'sentry/views/performance/database/useIneligibleProjects';
|
|
|
-import {useProjectSpanMetricCounts} from 'sentry/views/starfish/queries/useProjectSpanMetricsCounts';
|
|
|
+import {useDenylistedProjects} from 'sentry/views/performance/database/useDenylistedProjects';
|
|
|
+import {useOutdatedSDKProjects} from 'sentry/views/performance/database/useOutdatedSDKProjects';
|
|
|
|
|
|
interface Props {
|
|
|
Wrapper?: React.ComponentType;
|
|
|
+ isDataAvailable?: boolean;
|
|
|
}
|
|
|
|
|
|
function DivWrapper(props) {
|
|
|
return <div {...props} />;
|
|
|
}
|
|
|
|
|
|
-export function NoDataMessage({Wrapper = DivWrapper}: Props) {
|
|
|
+export function NoDataMessage({Wrapper = DivWrapper, isDataAvailable}: Props) {
|
|
|
+ const organization = useOrganization();
|
|
|
const {selection, isReady: pageFilterIsReady} = usePageFilters();
|
|
|
|
|
|
const selectedProjectIds = selection.projects.map(projectId => projectId.toString());
|
|
|
|
|
|
- const {data: projectSpanMetricsCounts, isLoading: areSpanMetricCountsLoading} =
|
|
|
- useProjectSpanMetricCounts({
|
|
|
- query: 'span.module:db',
|
|
|
- statsPeriod: SAMPLE_STATS_PERIOD,
|
|
|
- enabled: pageFilterIsReady,
|
|
|
+ const {projects: outdatedProjects, isFetching: areOutdatedProjectsFetching} =
|
|
|
+ useOutdatedSDKProjects({
|
|
|
projectId: selectedProjectIds,
|
|
|
+ enabled: pageFilterIsReady && !isDataAvailable,
|
|
|
});
|
|
|
|
|
|
- const doesAnySelectedProjectHaveMetrics =
|
|
|
- sumBy(projectSpanMetricsCounts, 'count()') > 0;
|
|
|
-
|
|
|
- const {ineligibleProjects, isFetching: areIneligibleProjectsFetching} =
|
|
|
- useIneligibleProjects({
|
|
|
+ const {projects: denylistedProjects, isFetching: areDenylistProjectsFetching} =
|
|
|
+ useDenylistedProjects({
|
|
|
projectId: selectedProjectIds,
|
|
|
- enabled: pageFilterIsReady && !doesAnySelectedProjectHaveMetrics,
|
|
|
});
|
|
|
|
|
|
- const organization = useOrganization();
|
|
|
+ const isDataFetching = areOutdatedProjectsFetching || areDenylistProjectsFetching;
|
|
|
|
|
|
- const hasMoreIneligibleProjectsThanVisible =
|
|
|
- ineligibleProjects.length > MAX_LISTED_PROJECTS;
|
|
|
-
|
|
|
- if (areSpanMetricCountsLoading || areIneligibleProjectsFetching) {
|
|
|
+ if (isDataFetching) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- if (!projectSpanMetricsCounts) {
|
|
|
- return null;
|
|
|
- }
|
|
|
+ const hasAnyProblematicProjects =
|
|
|
+ (!areOutdatedProjectsFetching && outdatedProjects.length > 0) ||
|
|
|
+ (!areDenylistProjectsFetching && denylistedProjects.length > 0);
|
|
|
|
|
|
- if (doesAnySelectedProjectHaveMetrics) {
|
|
|
+ if (isDataAvailable && !hasAnyProblematicProjects) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
- const firstIneligibleProjects = ineligibleProjects.slice(0, MAX_LISTED_PROJECTS + 1);
|
|
|
-
|
|
|
return (
|
|
|
<Wrapper>
|
|
|
- {t('No queries found.')}{' '}
|
|
|
- {tct(
|
|
|
- 'Try updating your filters, or learn more about performance monitoring for queries in our [documentation:documentation].',
|
|
|
- {
|
|
|
- documentation: (
|
|
|
- <ExternalLink href="https://docs.sentry.io/product/performance/queries/" />
|
|
|
- ),
|
|
|
- }
|
|
|
- )}{' '}
|
|
|
- {ineligibleProjects.length > 0 &&
|
|
|
- tct('You may also be missing data due to outdated SDKs: [projectList]', {
|
|
|
- documentation: (
|
|
|
- <ExternalLink href="https://docs.sentry.io/product/performance/query-insights/" />
|
|
|
- ),
|
|
|
- projectList: (
|
|
|
- <Fragment>
|
|
|
- {firstIneligibleProjects.map((project, projectIndex) => {
|
|
|
- return (
|
|
|
- <span key={project.id}>
|
|
|
- <a
|
|
|
- href={normalizeUrl(
|
|
|
- `/organizations/${organization.slug}/projects/${project.slug}/`
|
|
|
- )}
|
|
|
- >
|
|
|
- {project.name}
|
|
|
- </a>
|
|
|
- {projectIndex < firstIneligibleProjects.length - 1 && ', '}
|
|
|
- </span>
|
|
|
- );
|
|
|
- })}
|
|
|
- </Fragment>
|
|
|
- ),
|
|
|
- })}
|
|
|
- {hasMoreIneligibleProjectsThanVisible &&
|
|
|
- tct(' and [count] more.', {
|
|
|
- count: ineligibleProjects.length - MAX_LISTED_PROJECTS,
|
|
|
+ {!isDataAvailable &&
|
|
|
+ tct(
|
|
|
+ 'No queries found. Try updating your filters, or learn more about performance monitoring for queries in our [documentation:documentation].',
|
|
|
+ {
|
|
|
+ documentation: (
|
|
|
+ <ExternalLink href="https://docs.sentry.io/product/performance/queries/" />
|
|
|
+ ),
|
|
|
+ }
|
|
|
+ )}{' '}
|
|
|
+ {outdatedProjects.length > 0 &&
|
|
|
+ tct('You may be missing data due to outdated SDKs: [projectList].', {
|
|
|
+ projectList: <ProjectList projects={outdatedProjects} />,
|
|
|
})}{' '}
|
|
|
+ {denylistedProjects.length > 0 &&
|
|
|
+ tct(
|
|
|
+ 'Some of your projects have been omitted from query performance analysis. Please [supportLink]. Omitted projects: [projectList].',
|
|
|
+ {
|
|
|
+ supportLink: (
|
|
|
+ <Button priority="link" onClick={() => openHelpSearchModal({organization})}>
|
|
|
+ {t('Contact Support')}
|
|
|
+ </Button>
|
|
|
+ ),
|
|
|
+ projectList: <ProjectList projects={denylistedProjects} />,
|
|
|
+ }
|
|
|
+ )}
|
|
|
</Wrapper>
|
|
|
);
|
|
|
}
|
|
|
|
|
|
-const MAX_LISTED_PROJECTS = 3;
|
|
|
+interface ProjectListProps {
|
|
|
+ projects: Project[];
|
|
|
+ limit?: number;
|
|
|
+}
|
|
|
|
|
|
-const SAMPLE_STATS_PERIOD = '14d'; // The time period in which to check for any presence of span metrics
|
|
|
+function ProjectList({projects, limit = MAX_LISTED_PROJECTS}: ProjectListProps) {
|
|
|
+ const organization = useOrganization();
|
|
|
+
|
|
|
+ const visibleProjects = projects.slice(0, limit + 1);
|
|
|
+ const hasMoreProjectsThanVisible = projects.length > MAX_LISTED_PROJECTS;
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Fragment>
|
|
|
+ {visibleProjects.slice(0, limit).map((project, projectIndex) => {
|
|
|
+ return (
|
|
|
+ <span key={project.id}>
|
|
|
+ <a
|
|
|
+ href={normalizeUrl(
|
|
|
+ `/organizations/${organization.slug}/projects/${project.slug}/`
|
|
|
+ )}
|
|
|
+ >
|
|
|
+ {project.name}
|
|
|
+ </a>
|
|
|
+ {projectIndex < visibleProjects.length - 1 && ', '}
|
|
|
+ </span>
|
|
|
+ );
|
|
|
+ })}
|
|
|
+ {hasMoreProjectsThanVisible &&
|
|
|
+ tct(' and [count] more.', {
|
|
|
+ count: projects.length - limit,
|
|
|
+ })}{' '}
|
|
|
+ </Fragment>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+const MAX_LISTED_PROJECTS = 3;
|