members.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import * as Sentry from '@sentry/react';
  2. import MemberActions from 'sentry/actions/memberActions';
  3. import {Client} from 'sentry/api';
  4. import MemberListStore from 'sentry/stores/memberListStore';
  5. import {Member} from 'sentry/types';
  6. function getMemberUser(member: Member) {
  7. return {
  8. ...member.user,
  9. role: member.role,
  10. };
  11. }
  12. export async function fetchOrgMembers(
  13. api: Client,
  14. orgId: string,
  15. projectIds: string[] | null = null
  16. ) {
  17. const endpoint = `/organizations/${orgId}/users/`;
  18. const query = projectIds ? {project: projectIds} : {};
  19. try {
  20. const members = await api.requestPromise(endpoint, {method: 'GET', query});
  21. if (!members) {
  22. // This shouldn't happen if the request was successful
  23. // It should at least be an empty list
  24. Sentry.withScope(scope => {
  25. scope.setExtras({
  26. orgId,
  27. projectIds,
  28. });
  29. Sentry.captureException(new Error('Members is undefined'));
  30. });
  31. }
  32. const memberUsers = members?.filter(({user}: Member) => user);
  33. if (!memberUsers) {
  34. return [];
  35. }
  36. // Update the store with just the users, as avatars rely on them.
  37. MemberListStore.loadInitialData(memberUsers.map(getMemberUser));
  38. return members;
  39. } catch (err) {
  40. Sentry.setExtras({
  41. resp: err,
  42. });
  43. Sentry.captureException(err);
  44. }
  45. return [];
  46. }
  47. type IndexedMembersByProject = Record<string, Member['user'][]>;
  48. /**
  49. * Convert a list of members with user & project data
  50. * into a object that maps project slugs : users in that project.
  51. */
  52. export function indexMembersByProject(members: Member[]): IndexedMembersByProject {
  53. return members.reduce((acc, member) => {
  54. for (const project of member.projects) {
  55. if (!acc.hasOwnProperty(project)) {
  56. acc[project] = [];
  57. }
  58. acc[project].push(member.user);
  59. }
  60. return acc;
  61. }, {});
  62. }
  63. type UpdateMemberOptions = {
  64. data: Member | null;
  65. memberId: string;
  66. orgId: string;
  67. };
  68. export async function updateMember(
  69. api: Client,
  70. {orgId, memberId, data}: UpdateMemberOptions
  71. ) {
  72. MemberActions.update(memberId, data);
  73. const endpoint = `/organizations/${orgId}/members/${memberId}/`;
  74. try {
  75. const resp = await api.requestPromise(endpoint, {
  76. method: 'PUT',
  77. data,
  78. });
  79. MemberActions.updateSuccess(resp);
  80. return resp;
  81. } catch (err) {
  82. MemberActions.updateError(err);
  83. throw err;
  84. }
  85. }
  86. type ResendMemberInviteOptions = {
  87. memberId: string;
  88. orgId: string;
  89. data?: object;
  90. regenerate?: boolean;
  91. };
  92. export async function resendMemberInvite(
  93. api: Client,
  94. {orgId, memberId, regenerate, data}: ResendMemberInviteOptions
  95. ) {
  96. MemberActions.resendMemberInvite(orgId, data);
  97. const endpoint = `/organizations/${orgId}/members/${memberId}/`;
  98. try {
  99. const resp = await api.requestPromise(endpoint, {
  100. method: 'PUT',
  101. data: {
  102. regenerate,
  103. reinvite: true,
  104. },
  105. });
  106. MemberActions.resendMemberInviteSuccess(resp);
  107. return resp;
  108. } catch (err) {
  109. MemberActions.resendMemberInviteError(err);
  110. throw err;
  111. }
  112. }
  113. export function getCurrentMember(api: Client, orgId: string) {
  114. return api.requestPromise(`/organizations/${orgId}/members/me/`);
  115. }