organization.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import * as Sentry from '@sentry/react';
  2. import {addErrorMessage} from 'sentry/actionCreators/indicator';
  3. import {setActiveOrganization} from 'sentry/actionCreators/organizations';
  4. import GlobalSelectionActions from 'sentry/actions/globalSelectionActions';
  5. import OrganizationActions from 'sentry/actions/organizationActions';
  6. import ProjectActions from 'sentry/actions/projectActions';
  7. import TeamActions from 'sentry/actions/teamActions';
  8. import {Client, ResponseMeta} from 'sentry/api';
  9. import {Organization, Project, Team} from 'sentry/types';
  10. import {getPreloadedDataPromise} from 'sentry/utils/getPreloadedData';
  11. async function fetchOrg(
  12. api: Client,
  13. slug: string,
  14. isInitialFetch?: boolean
  15. ): Promise<Organization> {
  16. const [org] = await getPreloadedDataPromise(
  17. 'organization',
  18. slug,
  19. () =>
  20. // This data should get preloaded in static/sentry/index.ejs
  21. // If this url changes make sure to update the preload
  22. api.requestPromise(`/organizations/${slug}/`, {
  23. includeAllArgs: true,
  24. query: {detailed: 0},
  25. }),
  26. isInitialFetch
  27. );
  28. if (!org) {
  29. throw new Error('retrieved organization is falsey');
  30. }
  31. OrganizationActions.update(org, {replace: true});
  32. setActiveOrganization(org);
  33. return org;
  34. }
  35. async function fetchProjectsAndTeams(
  36. slug: string,
  37. isInitialFetch?: boolean
  38. ): Promise<
  39. [
  40. [Project[], string | undefined, XMLHttpRequest | ResponseMeta | undefined],
  41. [Team[], string | undefined, XMLHttpRequest | ResponseMeta | undefined]
  42. ]
  43. > {
  44. // Create a new client so the request is not cancelled
  45. const uncancelableApi = new Client();
  46. const projectsPromise = getPreloadedDataPromise(
  47. 'projects',
  48. slug,
  49. () =>
  50. // This data should get preloaded in static/sentry/index.ejs
  51. // If this url changes make sure to update the preload
  52. uncancelableApi.requestPromise(`/organizations/${slug}/projects/`, {
  53. includeAllArgs: true,
  54. query: {
  55. all_projects: 1,
  56. collapse: 'latestDeploys',
  57. },
  58. }),
  59. isInitialFetch
  60. );
  61. const teamsPromise = getPreloadedDataPromise(
  62. 'teams',
  63. slug,
  64. // This data should get preloaded in static/sentry/index.ejs
  65. // If this url changes make sure to update the preload
  66. () =>
  67. uncancelableApi.requestPromise(`/organizations/${slug}/teams/`, {
  68. includeAllArgs: true,
  69. }),
  70. isInitialFetch
  71. );
  72. try {
  73. return await Promise.all([projectsPromise, teamsPromise]);
  74. } catch (err) {
  75. // It's possible these requests fail with a 403 if the user has a role with
  76. // insufficient access to projects and teams, but *can* access org details
  77. // (e.g. billing). An example of this is in org settings.
  78. //
  79. // Ignore 403s and bubble up other API errors
  80. if (err.status !== 403) {
  81. throw err;
  82. }
  83. }
  84. return [
  85. [[], undefined, undefined],
  86. [[], undefined, undefined],
  87. ];
  88. }
  89. /**
  90. * Fetches an organization's details
  91. *
  92. * @param api A reference to the api client
  93. * @param slug The organization slug
  94. * @param silent Should we silently update the organization (do not clear the
  95. * current organization in the store)
  96. */
  97. export async function fetchOrganizationDetails(
  98. api: Client,
  99. slug: string,
  100. silent: boolean,
  101. isInitialFetch?: boolean
  102. ) {
  103. if (!silent) {
  104. OrganizationActions.reset();
  105. ProjectActions.reset();
  106. GlobalSelectionActions.reset();
  107. }
  108. const loadOrganization = async () => {
  109. try {
  110. await fetchOrg(api, slug, isInitialFetch);
  111. } catch (err) {
  112. if (!err) {
  113. return;
  114. }
  115. OrganizationActions.fetchOrgError(err);
  116. if (err.status === 403 || err.status === 401) {
  117. const errMessage =
  118. typeof err.responseJSON?.detail === 'string'
  119. ? err.responseJSON?.detail
  120. : typeof err.responseJSON?.detail?.message === 'string'
  121. ? err.responseJSON?.detail.message
  122. : null;
  123. if (errMessage) {
  124. addErrorMessage(errMessage);
  125. }
  126. return;
  127. }
  128. Sentry.captureException(err);
  129. }
  130. };
  131. const loadTeamsAndProjects = async () => {
  132. const [[projects], [teams]] = await fetchProjectsAndTeams(slug, isInitialFetch);
  133. ProjectActions.loadProjects(projects);
  134. TeamActions.loadTeams(teams);
  135. };
  136. return Promise.all([loadOrganization(), loadTeamsAndProjects()]);
  137. }