builtInRepositories.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import styled from '@emotion/styled';
  2. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  3. import {Client} from 'sentry/api';
  4. import Access from 'sentry/components/acl/access';
  5. import SelectField from 'sentry/components/forms/fields/selectField';
  6. import LoadingIndicator from 'sentry/components/loadingIndicator';
  7. import Panel from 'sentry/components/panels/panel';
  8. import PanelBody from 'sentry/components/panels/panelBody';
  9. import PanelHeader from 'sentry/components/panels/panelHeader';
  10. import {t} from 'sentry/locale';
  11. import ProjectsStore from 'sentry/stores/projectsStore';
  12. import {Organization, Project} from 'sentry/types';
  13. import {BuiltinSymbolSource} from 'sentry/types/debugFiles';
  14. const SECTION_TITLE = t('Built-in Repositories');
  15. type Props = {
  16. api: Client;
  17. builtinSymbolSourceOptions: BuiltinSymbolSource[];
  18. builtinSymbolSources: string[];
  19. isLoading: boolean;
  20. organization: Organization;
  21. projSlug: Project['slug'];
  22. };
  23. function BuiltInRepositories({
  24. api,
  25. organization,
  26. builtinSymbolSourceOptions,
  27. builtinSymbolSources,
  28. projSlug,
  29. isLoading,
  30. }: Props) {
  31. // If the project details object has an unknown built-in source, this will be filtered here.
  32. // This prevents the UI from showing the wrong feedback message when updating the field
  33. const validBuiltInSymbolSources = builtinSymbolSources.filter(builtinSymbolSource =>
  34. builtinSymbolSourceOptions.find(({sentry_key}) => sentry_key === builtinSymbolSource)
  35. );
  36. function getRequestMessages(builtinSymbolSourcesQuantity: number) {
  37. if (builtinSymbolSourcesQuantity === 0) {
  38. return {
  39. errorMessage: t('This field requires at least one built-in repository'),
  40. };
  41. }
  42. if (builtinSymbolSourcesQuantity > validBuiltInSymbolSources.length) {
  43. return {
  44. successMessage: t('Successfully added built-in repository'),
  45. errorMessage: t('An error occurred while adding new built-in repository'),
  46. };
  47. }
  48. return {
  49. successMessage: t('Successfully removed built-in repository'),
  50. errorMessage: t('An error occurred while removing built-in repository'),
  51. };
  52. }
  53. async function handleChange(value: null | string[]) {
  54. const {successMessage, errorMessage} = getRequestMessages((value ?? []).length);
  55. try {
  56. const updatedProjectDetails: Project = await api.requestPromise(
  57. `/projects/${organization.slug}/${projSlug}/`,
  58. {
  59. method: 'PUT',
  60. data: {
  61. builtinSymbolSources: value,
  62. },
  63. }
  64. );
  65. ProjectsStore.onUpdateSuccess(updatedProjectDetails);
  66. addSuccessMessage(successMessage);
  67. } catch {
  68. addErrorMessage(errorMessage);
  69. }
  70. }
  71. return (
  72. <Panel>
  73. <PanelHeader>{SECTION_TITLE}</PanelHeader>
  74. <PanelBody>
  75. {isLoading ? (
  76. <LoadingIndicator />
  77. ) : (
  78. <Access access={['project:write']}>
  79. {({hasAccess}) => (
  80. <StyledSelectField
  81. disabledReason={
  82. !hasAccess
  83. ? t(
  84. 'You do not have permission to edit built-in repositories configurations.'
  85. )
  86. : undefined
  87. }
  88. disabled={!hasAccess}
  89. name="builtinSymbolSources"
  90. label={SECTION_TITLE}
  91. help={t(
  92. 'Configures which built-in repositories Sentry should use to resolve debug files.'
  93. )}
  94. placeholder={t('Select built-in repository')}
  95. value={validBuiltInSymbolSources}
  96. onChange={handleChange}
  97. options={builtinSymbolSourceOptions
  98. .filter(source => !source.hidden)
  99. .map(source => ({
  100. value: source.sentry_key,
  101. label: source.name,
  102. }))}
  103. getValue={value => (value === null ? [] : value)}
  104. flexibleControlStateSize
  105. multiple
  106. />
  107. )}
  108. </Access>
  109. )}
  110. </PanelBody>
  111. </Panel>
  112. );
  113. }
  114. export default BuiltInRepositories;
  115. const StyledSelectField = styled(SelectField)`
  116. ${p => p.disabled && `cursor: not-allowed`}
  117. `;