profileGroupProvider.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import {createContext, useContext, useEffect, useState} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import {Client} from 'sentry/api';
  4. import {ProfileHeader} from 'sentry/components/profiling/profileHeader';
  5. import {t} from 'sentry/locale';
  6. import type {EventTransaction, Organization, Project} from 'sentry/types';
  7. import {RequestState} from 'sentry/types/core';
  8. import {useSentryEvent} from 'sentry/utils/profiling/hooks/useSentryEvent';
  9. import {importProfile, ProfileGroup} from 'sentry/utils/profiling/profile/importProfile';
  10. import useApi from 'sentry/utils/useApi';
  11. import useOrganization from 'sentry/utils/useOrganization';
  12. import {useParams} from 'sentry/utils/useParams';
  13. function fetchFlamegraphs(
  14. api: Client,
  15. eventId: string,
  16. projectId: Project['id'],
  17. organization: Organization
  18. ): Promise<ProfileGroup> {
  19. return api
  20. .requestPromise(
  21. `/projects/${organization.slug}/${projectId}/profiling/profiles/${eventId}/`,
  22. {
  23. method: 'GET',
  24. includeAllArgs: true,
  25. }
  26. )
  27. .then(([data]) => importProfile(data, eventId));
  28. }
  29. interface FlamegraphViewProps {
  30. children: React.ReactNode;
  31. }
  32. type ProfileGroupContextValue = RequestState<ProfileGroup>;
  33. type SetProfileGroupContextValue = React.Dispatch<
  34. React.SetStateAction<RequestState<ProfileGroup>>
  35. >;
  36. const ProfileGroupContext = createContext<ProfileGroupContextValue | null>(null);
  37. const SetProfileGroupContext = createContext<SetProfileGroupContextValue | null>(null);
  38. export function useProfileGroup() {
  39. const context = useContext(ProfileGroupContext);
  40. if (!context) {
  41. throw new Error('useProfileGroup was called outside of ProfileGroupProvider');
  42. }
  43. return context;
  44. }
  45. export function useSetProfileGroup() {
  46. const context = useContext(SetProfileGroupContext);
  47. if (!context) {
  48. throw new Error('useSetProfileGroup was called outside of SetProfileGroupProvider');
  49. }
  50. return context;
  51. }
  52. const ProfileTransactionContext =
  53. createContext<RequestState<EventTransaction | null> | null>(null);
  54. export function useProfileTransaction() {
  55. const context = useContext(ProfileTransactionContext);
  56. if (!context) {
  57. throw new Error(
  58. 'useProfileTransaction was called outside of ProfileTransactionContext'
  59. );
  60. }
  61. return context;
  62. }
  63. function ProfileGroupProvider(props: FlamegraphViewProps): React.ReactElement {
  64. const api = useApi();
  65. const organization = useOrganization();
  66. const params = useParams();
  67. const [profileGroupState, setProfileGroupState] = useState<RequestState<ProfileGroup>>({
  68. type: 'initial',
  69. });
  70. const profileTransaction = useSentryEvent<EventTransaction>(
  71. params.orgId,
  72. params.projectId,
  73. profileGroupState.type === 'resolved' ? profileGroupState.data.transactionID : null
  74. );
  75. useEffect(() => {
  76. if (!params.eventId || !params.projectId) {
  77. return undefined;
  78. }
  79. setProfileGroupState({type: 'loading'});
  80. fetchFlamegraphs(api, params.eventId, params.projectId, organization)
  81. .then(importedFlamegraphs => {
  82. setProfileGroupState({type: 'resolved', data: importedFlamegraphs});
  83. })
  84. .catch(err => {
  85. const message = err.toString() || t('Error: Unable to load profiles');
  86. setProfileGroupState({type: 'errored', error: message});
  87. Sentry.captureException(err);
  88. });
  89. return () => {
  90. api.clear();
  91. };
  92. }, [params.eventId, params.projectId, api, organization]);
  93. return (
  94. <ProfileGroupContext.Provider value={profileGroupState}>
  95. <SetProfileGroupContext.Provider value={setProfileGroupState}>
  96. <ProfileTransactionContext.Provider value={profileTransaction}>
  97. <ProfileHeader
  98. eventId={params.eventId}
  99. projectId={params.projectId}
  100. transaction={
  101. profileTransaction.type === 'resolved' ? profileTransaction.data : null
  102. }
  103. />
  104. {props.children}
  105. </ProfileTransactionContext.Provider>
  106. </SetProfileGroupContext.Provider>
  107. </ProfileGroupContext.Provider>
  108. );
  109. }
  110. export default ProfileGroupProvider;