profileGroupProvider.tsx 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  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 {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. const ProfileGroupContext = createContext<
  33. | [
  34. RequestState<ProfileGroup>,
  35. React.Dispatch<React.SetStateAction<RequestState<ProfileGroup>>>
  36. ]
  37. | null
  38. >(null);
  39. export const useProfileGroup = () => {
  40. const context = useContext(ProfileGroupContext);
  41. if (!context) {
  42. throw new Error('useProfileGroup was called outside of ProfileGroupProvider');
  43. }
  44. return context;
  45. };
  46. function ProfileGroupProvider(props: FlamegraphViewProps): React.ReactElement {
  47. const api = useApi();
  48. const organization = useOrganization();
  49. const params = useParams();
  50. const [profileGroupState, setProfileGroupState] = useState<RequestState<ProfileGroup>>({
  51. type: 'initial',
  52. });
  53. useEffect(() => {
  54. if (!params.eventId || !params.projectId) {
  55. return undefined;
  56. }
  57. setProfileGroupState({type: 'loading'});
  58. fetchFlamegraphs(api, params.eventId, params.projectId, organization)
  59. .then(importedFlamegraphs => {
  60. setProfileGroupState({type: 'resolved', data: importedFlamegraphs});
  61. })
  62. .catch(err => {
  63. const message = err.toString() || t('Error: Unable to load profiles');
  64. setProfileGroupState({type: 'errored', error: message});
  65. Sentry.captureException(err);
  66. });
  67. return () => {
  68. api.clear();
  69. };
  70. }, [params.eventId, params.projectId, api, organization]);
  71. const transactionEvent = useSentryEvent(
  72. params.orgId,
  73. params.projectId,
  74. profileGroupState.type === 'resolved' ? profileGroupState.data.transactionID : null
  75. );
  76. return (
  77. <ProfileGroupContext.Provider value={[profileGroupState, setProfileGroupState]}>
  78. <ProfileHeader
  79. transaction={transactionEvent.type === 'resolved' ? transactionEvent.data : null}
  80. />
  81. {props.children}
  82. </ProfileGroupContext.Provider>
  83. );
  84. }
  85. export default ProfileGroupProvider;