members.tsx 2.6 KB

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