organizations.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. import {browserHistory} from 'react-router';
  2. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  3. import {resetPageFilters} from 'sentry/actionCreators/pageFilters';
  4. import {Client} from 'sentry/api';
  5. import GuideStore from 'sentry/stores/guideStore';
  6. import LatestContextStore from 'sentry/stores/latestContextStore';
  7. import OrganizationsStore from 'sentry/stores/organizationsStore';
  8. import OrganizationStore from 'sentry/stores/organizationStore';
  9. import ProjectsStore from 'sentry/stores/projectsStore';
  10. import TeamStore from 'sentry/stores/teamStore';
  11. import {Organization} from 'sentry/types';
  12. type RedirectRemainingOrganizationParams = {
  13. /**
  14. * The organization slug
  15. */
  16. orgId: string;
  17. /**
  18. * Should remove org?
  19. */
  20. removeOrg?: boolean;
  21. };
  22. /**
  23. * After removing an organization, this will redirect to a remaining active organization or
  24. * the screen to create a new organization.
  25. *
  26. * Can optionally remove organization from organizations store.
  27. */
  28. export function redirectToRemainingOrganization({
  29. orgId,
  30. removeOrg,
  31. }: RedirectRemainingOrganizationParams) {
  32. // Remove queued, should redirect
  33. const allOrgs = OrganizationsStore.getAll().filter(
  34. org => org.status.id === 'active' && org.slug !== orgId
  35. );
  36. if (!allOrgs.length) {
  37. browserHistory.push('/organizations/new/');
  38. return;
  39. }
  40. // Let's be smart and select the best org to redirect to
  41. const firstRemainingOrg = allOrgs[0];
  42. // TODO(domains) Need to find a good way to handle this. useResolveRoute()
  43. // doesn't currently work because it is a hook.
  44. browserHistory.push(`/organizations/${firstRemainingOrg.slug}/issues/`);
  45. // Remove org from SidebarDropdown
  46. if (removeOrg) {
  47. OrganizationsStore.remove(orgId);
  48. }
  49. }
  50. type RemoveParams = {
  51. /**
  52. * The organization slug
  53. */
  54. orgId: string;
  55. /**
  56. * An optional error message to be used in a toast, if remove fails
  57. */
  58. errorMessage?: string;
  59. /**
  60. * An optional success message to be used in a toast, if remove succeeds
  61. */
  62. successMessage?: string;
  63. };
  64. export function remove(api: Client, {successMessage, errorMessage, orgId}: RemoveParams) {
  65. const endpoint = `/organizations/${orgId}/`;
  66. return api
  67. .requestPromise(endpoint, {
  68. method: 'DELETE',
  69. })
  70. .then(() => {
  71. OrganizationsStore.onRemoveSuccess(orgId);
  72. if (successMessage) {
  73. addSuccessMessage(successMessage);
  74. }
  75. })
  76. .catch(() => {
  77. if (errorMessage) {
  78. addErrorMessage(errorMessage);
  79. }
  80. });
  81. }
  82. export function switchOrganization() {
  83. resetPageFilters();
  84. }
  85. export function removeAndRedirectToRemainingOrganization(
  86. api: Client,
  87. params: RedirectRemainingOrganizationParams & RemoveParams
  88. ) {
  89. remove(api, params).then(() => redirectToRemainingOrganization(params));
  90. }
  91. /**
  92. * Set active organization
  93. */
  94. export function setActiveOrganization(org: Organization) {
  95. GuideStore.setActiveOrganization(org);
  96. LatestContextStore.onSetActiveOrganization(org);
  97. }
  98. export function changeOrganizationSlug(
  99. prev: Organization,
  100. next: Partial<Organization> & Pick<Organization, 'slug'>
  101. ) {
  102. OrganizationsStore.onChangeSlug(prev, next);
  103. }
  104. /**
  105. * Updates an organization for the store
  106. *
  107. * Accepts a partial organization as it will merge will existing organization
  108. */
  109. export function updateOrganization(org: Partial<Organization>) {
  110. OrganizationsStore.onUpdate(org);
  111. OrganizationStore.onUpdate(org);
  112. }
  113. type FetchOrganizationByMemberParams = {
  114. addOrg?: boolean;
  115. fetchOrgDetails?: boolean;
  116. };
  117. export async function fetchOrganizationByMember(
  118. api: Client,
  119. memberId: string,
  120. {addOrg, fetchOrgDetails}: FetchOrganizationByMemberParams
  121. ) {
  122. const data = await api.requestPromise(`/organizations/?query=member_id:${memberId}`);
  123. if (!data.length) {
  124. return null;
  125. }
  126. const org = data[0];
  127. if (addOrg) {
  128. // add org to SwitchOrganization dropdown
  129. OrganizationsStore.addOrReplace(org);
  130. }
  131. if (fetchOrgDetails) {
  132. // load SidebarDropdown with org details including `access`
  133. await fetchOrganizationDetails(api, org.slug, {setActive: true, loadProjects: true});
  134. }
  135. return org;
  136. }
  137. type FetchOrganizationDetailsParams = {
  138. /**
  139. * Should load projects in ProjectsStore
  140. */
  141. loadProjects?: boolean;
  142. /**
  143. * Should load teams in TeamStore?
  144. */
  145. loadTeam?: boolean;
  146. /**
  147. * Should set as active organization?
  148. */
  149. setActive?: boolean;
  150. };
  151. export async function fetchOrganizationDetails(
  152. api: Client,
  153. orgId: string,
  154. {setActive, loadProjects, loadTeam}: FetchOrganizationDetailsParams
  155. ) {
  156. const data = await api.requestPromise(`/organizations/${orgId}/`);
  157. if (setActive) {
  158. setActiveOrganization(data);
  159. }
  160. if (loadTeam) {
  161. TeamStore.loadInitialData(data.teams, false, null);
  162. }
  163. if (loadProjects) {
  164. ProjectsStore.loadInitialData(data.projects || []);
  165. }
  166. return data;
  167. }