members.tsx 2.7 KB

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