releaseDetailsRequest.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import * as React from 'react';
  2. import {Location} from 'history';
  3. import isEqual from 'lodash/isEqual';
  4. import {addErrorMessage} from 'app/actionCreators/indicator';
  5. import {Client} from 'app/api';
  6. import {t} from 'app/locale';
  7. import {Organization, SessionApiResponse} from 'app/types';
  8. import {filterSessionsInTimeWindow, getSessionsInterval} from 'app/utils/sessions';
  9. import withApi from 'app/utils/withApi';
  10. import {getReleaseParams, ReleaseBounds} from '../../utils';
  11. export type ReleaseHealthRequestRenderProps = {
  12. loading: boolean;
  13. reloading: boolean;
  14. errored: boolean;
  15. thisRelease: SessionApiResponse | null;
  16. allReleases: SessionApiResponse | null;
  17. };
  18. type Props = {
  19. api: Client;
  20. organization: Organization;
  21. children: (renderProps: ReleaseHealthRequestRenderProps) => React.ReactNode;
  22. location: Location;
  23. version: string;
  24. releaseBounds: ReleaseBounds;
  25. disable?: boolean;
  26. };
  27. type State = {
  28. reloading: boolean;
  29. errored: boolean;
  30. thisRelease: SessionApiResponse | null;
  31. allReleases: SessionApiResponse | null;
  32. };
  33. class ReleaseDetailsRequest extends React.Component<Props, State> {
  34. state: State = {
  35. reloading: false,
  36. errored: false,
  37. thisRelease: null,
  38. allReleases: null,
  39. };
  40. componentDidMount() {
  41. this.fetchData();
  42. }
  43. componentDidUpdate(prevProps: Props) {
  44. if (
  45. prevProps.version !== this.props.version ||
  46. !isEqual(prevProps.location, this.props.location)
  47. ) {
  48. this.fetchData();
  49. }
  50. }
  51. get path() {
  52. const {organization} = this.props;
  53. return `/organizations/${organization.slug}/sessions/`;
  54. }
  55. get baseQueryParams() {
  56. const {location, releaseBounds, organization} = this.props;
  57. const releaseParams = getReleaseParams({
  58. location,
  59. releaseBounds,
  60. });
  61. return {
  62. field: ['count_unique(user)', 'sum(session)', 'p50(session.duration)'],
  63. groupBy: ['session.status'],
  64. interval: getSessionsInterval(
  65. {
  66. start: releaseParams.start,
  67. end: releaseParams.end,
  68. period: releaseParams.statsPeriod ?? undefined,
  69. },
  70. {highFidelity: organization.features.includes('minute-resolution-sessions')}
  71. ),
  72. ...releaseParams,
  73. };
  74. }
  75. fetchData = async () => {
  76. const {api, disable} = this.props;
  77. if (disable) {
  78. return;
  79. }
  80. api.clear();
  81. this.setState(state => ({
  82. reloading: state.thisRelease !== null && state.allReleases !== null,
  83. errored: false,
  84. }));
  85. const promises = [this.fetchThisRelease(), this.fetchAllReleases()];
  86. try {
  87. const [thisRelease, allReleases] = await Promise.all(promises);
  88. this.setState({
  89. reloading: false,
  90. thisRelease: filterSessionsInTimeWindow(
  91. thisRelease,
  92. this.baseQueryParams.start,
  93. this.baseQueryParams.end
  94. ),
  95. allReleases: filterSessionsInTimeWindow(
  96. allReleases,
  97. this.baseQueryParams.start,
  98. this.baseQueryParams.end
  99. ),
  100. });
  101. } catch (error) {
  102. addErrorMessage(error.responseJSON?.detail ?? t('Error loading health data'));
  103. this.setState({
  104. reloading: false,
  105. errored: true,
  106. });
  107. }
  108. };
  109. async fetchThisRelease() {
  110. const {api, version} = this.props;
  111. const response: SessionApiResponse = await api.requestPromise(this.path, {
  112. query: {
  113. ...this.baseQueryParams,
  114. query: `release:"${version}"`,
  115. },
  116. });
  117. return response;
  118. }
  119. async fetchAllReleases() {
  120. const {api} = this.props;
  121. const response: SessionApiResponse = await api.requestPromise(this.path, {
  122. query: this.baseQueryParams,
  123. });
  124. return response;
  125. }
  126. render() {
  127. const {reloading, errored, thisRelease, allReleases} = this.state;
  128. const {children} = this.props;
  129. const loading = thisRelease === null && allReleases === null;
  130. return children({
  131. loading,
  132. reloading,
  133. errored,
  134. thisRelease,
  135. allReleases,
  136. });
  137. }
  138. }
  139. export default withApi(ReleaseDetailsRequest);