useHeartbeat.tsx 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. import {useState} from 'react';
  2. import {
  3. Group,
  4. Project,
  5. SessionApiResponse,
  6. SessionFieldWithOperation,
  7. } from 'sentry/types';
  8. import {useQuery} from 'sentry/utils/queryClient';
  9. import {getCount} from 'sentry/utils/sessions';
  10. import useOrganization from 'sentry/utils/useOrganization';
  11. const DEFAULT_POLL_INTERVAL = 5000;
  12. export function useHeartbeat(
  13. projectSlug: string | undefined,
  14. projectId: string | undefined
  15. ) {
  16. const organization = useOrganization();
  17. const [firstError, setFirstError] = useState<string | null>(null);
  18. const [firstTransactionReceived, setFirstTransactionReceived] = useState(false);
  19. const [sessionReceived, setSessionReceived] = useState(false);
  20. const [firstIssue, setFirstIssue] = useState<Group | undefined>(undefined);
  21. const serverConnected = sessionReceived || firstTransactionReceived;
  22. const {isLoading: eventIsLoading} = useQuery<Project>(
  23. [`/projects/${organization.slug}/${projectSlug}/`],
  24. {
  25. staleTime: 0,
  26. refetchInterval: DEFAULT_POLL_INTERVAL,
  27. enabled: !!projectSlug && !firstError, // Fetch only if the project is available and we have not yet received an error,
  28. onSuccess: data => {
  29. setFirstError(data.firstEvent);
  30. // When an error is received, a transaction is also received
  31. setFirstTransactionReceived(!!data.firstTransactionEvent);
  32. },
  33. }
  34. );
  35. const {isLoading: sessionIsLoading} = useQuery<SessionApiResponse>(
  36. [
  37. `/organizations/${organization.slug}/sessions/`,
  38. {
  39. query: {
  40. project: projectId,
  41. statsPeriod: '24h',
  42. field: [SessionFieldWithOperation.SESSIONS],
  43. },
  44. },
  45. ],
  46. {
  47. staleTime: 0,
  48. refetchInterval: DEFAULT_POLL_INTERVAL,
  49. enabled: !!projectId && !serverConnected, // Fetch only if the project is available and we if a connection to Sentry was not yet established,
  50. onSuccess: data => {
  51. const hasHealthData =
  52. getCount(data.groups, SessionFieldWithOperation.SESSIONS) > 0;
  53. setSessionReceived(hasHealthData);
  54. },
  55. }
  56. );
  57. // Locate the projects first issue group. The project.firstEvent field will
  58. // *not* include sample events, while just looking at the issues list will.
  59. // We will wait until the project.firstEvent is set and then locate the
  60. // event given that event datetime
  61. const {isLoading: issuesLoading} = useQuery<Group[]>(
  62. [`/projects/${organization.slug}/${projectSlug}/issues/`],
  63. {
  64. staleTime: 0,
  65. enabled: !!firstError && !firstIssue, // Only fetch if an error event is received and we have not yet located the first issue,
  66. onSuccess: data => {
  67. setFirstIssue(data.find((issue: Group) => issue.firstSeen === firstError));
  68. },
  69. }
  70. );
  71. const firstErrorReceived = firstIssue ?? !!firstError;
  72. const loading = eventIsLoading || sessionIsLoading;
  73. return {
  74. loading,
  75. issuesLoading,
  76. serverConnected,
  77. firstErrorReceived,
  78. firstTransactionReceived,
  79. sessionReceived,
  80. };
  81. }