transactionProfileIdProvider.tsx 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import {createContext, useContext, useEffect, useMemo} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import {PageFilters} from 'sentry/types';
  4. import {useProfileEvents} from 'sentry/utils/profiling/hooks/useProfileEvents';
  5. import useOrganization from 'sentry/utils/useOrganization';
  6. const TransactionProfileContext = createContext<string | null | undefined>(undefined);
  7. interface TransactionToProfileIdProviderProps {
  8. children: React.ReactNode;
  9. timestamp: string | undefined;
  10. transactionId: string | undefined;
  11. projectId?: string | undefined;
  12. }
  13. export function TransactionProfileIdProvider({
  14. projectId,
  15. timestamp,
  16. transactionId,
  17. children,
  18. }: TransactionToProfileIdProviderProps) {
  19. const organization = useOrganization();
  20. // create a 24h timeframe relative from the transaction timestamp to use for
  21. // the profile events query
  22. const datetime: PageFilters['datetime'] | undefined = useMemo(() => {
  23. if (!timestamp) {
  24. return undefined;
  25. }
  26. const ts = new Date(timestamp);
  27. const start = new Date(new Date(ts).setHours(ts.getHours() - 12));
  28. const end = new Date(new Date(ts).setHours(ts.getHours() + 12));
  29. return {
  30. start,
  31. end,
  32. period: null,
  33. utc: true,
  34. };
  35. }, [timestamp]);
  36. const profileIdColumn = organization.features.includes('profiling-using-transactions')
  37. ? 'profile.id'
  38. : 'id';
  39. const transactionIdColumn = organization.features.includes(
  40. 'profiling-using-transactions'
  41. )
  42. ? 'id'
  43. : 'trace.transaction';
  44. const {status, data, error} = useProfileEvents({
  45. projects: projectId ? [projectId] : undefined,
  46. fields: [profileIdColumn],
  47. referrer: 'transactionToProfileProvider',
  48. limit: 1,
  49. sort: {
  50. key: 'id',
  51. order: 'asc',
  52. },
  53. query: `${transactionIdColumn}:${transactionId}`,
  54. enabled: Boolean(transactionId),
  55. datetime,
  56. });
  57. useEffect(() => {
  58. if (status !== 'error') {
  59. return;
  60. }
  61. if (error.status !== 404) {
  62. Sentry.captureException(error);
  63. }
  64. }, [status, error]);
  65. const profileId = (data?.data[0]?.[profileIdColumn] as string | undefined) ?? null;
  66. return (
  67. <TransactionProfileContext.Provider value={profileId}>
  68. {children}
  69. </TransactionProfileContext.Provider>
  70. );
  71. }
  72. TransactionProfileIdProvider.Context = TransactionProfileContext;
  73. export function useTransactionProfileId() {
  74. const ctx = useContext(TransactionProfileContext);
  75. if (typeof ctx === 'undefined') {
  76. throw new Error(`useTransactionProfile called outside of TransactionProfileProvider`);
  77. }
  78. return ctx;
  79. }