integrationExternalUserMappings.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import {Fragment} from 'react';
  2. import {addErrorMessage, addSuccessMessage} from 'app/actionCreators/indicator';
  3. import {openModal} from 'app/actionCreators/modal';
  4. import AsyncComponent from 'app/components/asyncComponent';
  5. import IntegrationExternalMappingForm from 'app/components/integrationExternalMappingForm';
  6. import IntegrationExternalMappings from 'app/components/integrationExternalMappings';
  7. import {t} from 'app/locale';
  8. import {
  9. ExternalActorMapping,
  10. ExternalUser,
  11. Integration,
  12. Member,
  13. Organization,
  14. } from 'app/types';
  15. import withOrganization from 'app/utils/withOrganization';
  16. type Props = AsyncComponent['props'] & {
  17. integration: Integration;
  18. organization: Organization;
  19. };
  20. type State = AsyncComponent['state'] & {
  21. members: (Member & {externalUsers: ExternalUser[]})[];
  22. };
  23. class IntegrationExternalUserMappings extends AsyncComponent<Props, State> {
  24. getEndpoints(): ReturnType<AsyncComponent['getEndpoints']> {
  25. const {organization} = this.props;
  26. return [
  27. [
  28. 'members',
  29. `/organizations/${organization.slug}/members/`,
  30. {query: {query: 'hasExternalUsers:true', expand: 'externalUsers'}},
  31. ],
  32. ];
  33. }
  34. handleDelete = async (mapping: ExternalActorMapping) => {
  35. const {organization} = this.props;
  36. const endpoint = `/organizations/${organization.slug}/external-users/${mapping.id}/`;
  37. try {
  38. await this.api.requestPromise(endpoint, {
  39. method: 'DELETE',
  40. });
  41. // remove config and update state
  42. addSuccessMessage(t('Deletion successful'));
  43. this.fetchData();
  44. } catch {
  45. //no 4xx errors should happen on delete
  46. addErrorMessage(t('An error occurred'));
  47. }
  48. };
  49. handleSubmitSuccess = () => {
  50. // Don't bother updating state. The info is in array of objects for each object in another array of objects.
  51. // Easier and less error-prone to re-fetch the data and re-calculate state.
  52. this.fetchData();
  53. };
  54. get mappings() {
  55. const {integration} = this.props;
  56. const {members} = this.state;
  57. const externalUserMappings = members.reduce((acc, member) => {
  58. const {externalUsers, user} = member;
  59. acc.push(
  60. ...externalUsers
  61. .filter(externalUser => externalUser.provider === integration.provider.key)
  62. .map(externalUser => ({...externalUser, sentryName: user.name}))
  63. );
  64. return acc;
  65. }, [] as ExternalActorMapping[]);
  66. return externalUserMappings.sort((a, b) => parseInt(a.id, 10) - parseInt(b.id, 10));
  67. }
  68. sentryNamesMapper(members: Member[]) {
  69. return members
  70. .filter(member => member.user)
  71. .map(({user: {id}, email, name}) => {
  72. const label = email !== name ? `${name} - ${email}` : `${email}`;
  73. return {id, name: label};
  74. });
  75. }
  76. openModal = (mapping?: ExternalActorMapping) => {
  77. const {organization, integration} = this.props;
  78. openModal(({Body, Header, closeModal}) => (
  79. <Fragment>
  80. <Header closeButton>{t('Configure External User Mapping')}</Header>
  81. <Body>
  82. <IntegrationExternalMappingForm
  83. organization={organization}
  84. integration={integration}
  85. onSubmitSuccess={() => {
  86. this.handleSubmitSuccess();
  87. closeModal();
  88. }}
  89. mapping={mapping}
  90. sentryNamesMapper={this.sentryNamesMapper}
  91. type="user"
  92. url={`/organizations/${organization.slug}/members/`}
  93. onCancel={closeModal}
  94. baseEndpoint={`/organizations/${organization.slug}/external-users/`}
  95. />
  96. </Body>
  97. </Fragment>
  98. ));
  99. };
  100. renderBody() {
  101. const {integration} = this.props;
  102. return (
  103. <Fragment>
  104. <IntegrationExternalMappings
  105. integration={integration}
  106. type="user"
  107. mappings={this.mappings}
  108. onCreateOrEdit={this.openModal}
  109. onDelete={this.handleDelete}
  110. />
  111. </Fragment>
  112. );
  113. }
  114. }
  115. export default withOrganization(IntegrationExternalUserMappings);