builtInRepositories.tsx 4.3 KB

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