repositoryProjectPathConfigForm.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. import {Component} from 'react';
  2. import pick from 'lodash/pick';
  3. import {FieldFromConfig} from 'sentry/components/forms';
  4. import Form from 'sentry/components/forms/form';
  5. import {Field} from 'sentry/components/forms/type';
  6. import {t} from 'sentry/locale';
  7. import {
  8. Integration,
  9. Organization,
  10. Project,
  11. Repository,
  12. RepositoryProjectPathConfig,
  13. } from 'sentry/types';
  14. import {
  15. sentryNameToOption,
  16. trackIntegrationAnalytics,
  17. } from 'sentry/utils/integrationUtil';
  18. type Props = {
  19. integration: Integration;
  20. onCancel: Form['props']['onCancel'];
  21. onSubmitSuccess: Form['props']['onSubmitSuccess'];
  22. organization: Organization;
  23. projects: Project[];
  24. repos: Repository[];
  25. existingConfig?: RepositoryProjectPathConfig;
  26. };
  27. export default class RepositoryProjectPathConfigForm extends Component<Props> {
  28. get initialData() {
  29. const {existingConfig, integration} = this.props;
  30. return {
  31. defaultBranch: 'master',
  32. stackRoot: '',
  33. sourceRoot: '',
  34. repositoryId: existingConfig?.repoId,
  35. integrationId: integration.id,
  36. ...pick(existingConfig, ['projectId', 'defaultBranch', 'stackRoot', 'sourceRoot']),
  37. };
  38. }
  39. get formFields(): Field[] {
  40. const {projects, repos, organization} = this.props;
  41. const orgSlug = organization.slug;
  42. const repoChoices = repos.map(({name, id}) => ({value: id, label: name}));
  43. return [
  44. {
  45. name: 'projectId',
  46. type: 'sentry_project_selector',
  47. required: true,
  48. label: t('Project'),
  49. projects,
  50. },
  51. {
  52. name: 'repositoryId',
  53. type: 'select_async',
  54. required: true,
  55. label: t('Repo'),
  56. placeholder: t('Choose repo'),
  57. url: `/organizations/${orgSlug}/repos/`,
  58. defaultOptions: repoChoices,
  59. onResults: results => results.map(sentryNameToOption),
  60. },
  61. {
  62. name: 'defaultBranch',
  63. type: 'string',
  64. required: true,
  65. label: t('Branch'),
  66. placeholder: t('Type your branch'),
  67. showHelpInTooltip: true,
  68. help: t(
  69. 'If an event does not have a release tied to a commit, we will use this branch when linking to your source code.'
  70. ),
  71. },
  72. {
  73. name: 'stackRoot',
  74. type: 'string',
  75. required: false,
  76. label: t('Stack Trace Root'),
  77. placeholder: t('Type root path of your stack traces'),
  78. showHelpInTooltip: true,
  79. help: t(
  80. 'Any stack trace starting with this path will be mapped with this rule. An empty string will match all paths.'
  81. ),
  82. },
  83. {
  84. name: 'sourceRoot',
  85. type: 'string',
  86. required: false,
  87. label: t('Source Code Root'),
  88. placeholder: t('Type root path of your source code, e.g. `src/`.'),
  89. showHelpInTooltip: true,
  90. help: t(
  91. 'When a rule matches, the stack trace root is replaced with this path to get the path in your repository. Leaving this empty means replacing the stack trace root with an empty string.'
  92. ),
  93. },
  94. ];
  95. }
  96. handlePreSubmit() {
  97. trackIntegrationAnalytics('integrations.stacktrace_submit_config', {
  98. setup_type: 'manual',
  99. view: 'integration_configuration_detail',
  100. provider: this.props.integration.provider.key,
  101. organization: this.props.organization,
  102. });
  103. }
  104. render() {
  105. const {organization, onSubmitSuccess, onCancel, existingConfig} = this.props;
  106. // endpoint changes if we are making a new row or updating an existing one
  107. const baseEndpoint = `/organizations/${organization.slug}/code-mappings/`;
  108. const endpoint = existingConfig
  109. ? `${baseEndpoint}${existingConfig.id}/`
  110. : baseEndpoint;
  111. const apiMethod = existingConfig ? 'PUT' : 'POST';
  112. return (
  113. <Form
  114. onSubmitSuccess={onSubmitSuccess}
  115. onPreSubmit={() => this.handlePreSubmit()}
  116. initialData={this.initialData}
  117. apiEndpoint={endpoint}
  118. apiMethod={apiMethod}
  119. onCancel={onCancel}
  120. >
  121. {this.formFields.map(field => (
  122. <FieldFromConfig
  123. key={field.name}
  124. field={field}
  125. inline={false}
  126. stacked
  127. flexibleControlStateSize
  128. />
  129. ))}
  130. </Form>
  131. );
  132. }
  133. }