suspectCommits.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import {Fragment, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import uniqBy from 'lodash/uniqBy';
  4. import type {CommitRowProps} from 'sentry/components/commitRow';
  5. import {SuspectCommitHeader} from 'sentry/components/events/styles';
  6. import Panel from 'sentry/components/panels/panel';
  7. import {IconAdd, IconSubtract} from 'sentry/icons';
  8. import {t, tn} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import type {Group} from 'sentry/types/group';
  11. import type {Commit} from 'sentry/types/integrations';
  12. import type {AvatarProject} from 'sentry/types/project';
  13. import {trackAnalytics} from 'sentry/utils/analytics';
  14. import {getAnalyticsDataForGroup} from 'sentry/utils/events';
  15. import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
  16. import useCommitters from 'sentry/utils/useCommitters';
  17. import useOrganization from 'sentry/utils/useOrganization';
  18. interface Props {
  19. commitRow: React.ComponentType<CommitRowProps>;
  20. eventId: string;
  21. project: AvatarProject;
  22. group?: Group;
  23. }
  24. export function SuspectCommits({group, eventId, project, commitRow: CommitRow}: Props) {
  25. const organization = useOrganization();
  26. const [isExpanded, setIsExpanded] = useState(false);
  27. const {data} = useCommitters({
  28. eventId,
  29. projectSlug: project.slug,
  30. });
  31. const committers = data?.committers ?? [];
  32. function getUniqueCommitsWithAuthors() {
  33. // Get a list of commits with author information attached
  34. const commitsWithAuthors = committers.flatMap(({commits, author}) =>
  35. commits.map(commit => ({
  36. ...commit,
  37. author,
  38. }))
  39. );
  40. // Remove duplicate commits
  41. return uniqBy(commitsWithAuthors, commit => commit.id);
  42. }
  43. const commits = getUniqueCommitsWithAuthors();
  44. useRouteAnalyticsParams({
  45. num_suspect_commits: commits.length,
  46. suspect_commit_calculation: commits[0]?.suspectCommitType ?? 'no suspect commit',
  47. });
  48. if (!committers.length) {
  49. return null;
  50. }
  51. const handlePullRequestClick = (commit: Commit, commitIndex: number) => {
  52. trackAnalytics('issue_details.suspect_commits.pull_request_clicked', {
  53. organization,
  54. project_id: parseInt(project.id as string, 10),
  55. suspect_commit_calculation: commit.suspectCommitType ?? 'unknown',
  56. suspect_commit_index: commitIndex,
  57. ...getAnalyticsDataForGroup(group),
  58. });
  59. };
  60. const handleCommitClick = (commit: Commit, commitIndex: number) => {
  61. trackAnalytics('issue_details.suspect_commits.commit_clicked', {
  62. organization,
  63. project_id: parseInt(project.id as string, 10),
  64. has_pull_request: commit.pullRequest?.id !== undefined,
  65. suspect_commit_calculation: commit.suspectCommitType ?? 'unknown',
  66. suspect_commit_index: commitIndex,
  67. ...getAnalyticsDataForGroup(group),
  68. });
  69. };
  70. const commitHeading = tn('Suspect Commit', 'Suspect Commits (%s)', commits.length);
  71. return (
  72. <div>
  73. <SuspectCommitHeader>
  74. <h3 data-test-id="suspect-commit">{commitHeading}</h3>
  75. {commits.length > 1 && (
  76. <ExpandButton
  77. onClick={() => setIsExpanded(!isExpanded)}
  78. data-test-id="expand-commit-list"
  79. >
  80. {isExpanded ? (
  81. <Fragment>
  82. {t('Show less')} <IconSubtract isCircled size="md" />
  83. </Fragment>
  84. ) : (
  85. <Fragment>
  86. {t('Show more')} <IconAdd isCircled size="md" />
  87. </Fragment>
  88. )}
  89. </ExpandButton>
  90. )}
  91. </SuspectCommitHeader>
  92. <StyledPanel>
  93. {commits.slice(0, isExpanded ? 100 : 1).map((commit, commitIndex) => (
  94. <CommitRow
  95. key={commit.id}
  96. commit={commit}
  97. onCommitClick={() => handleCommitClick(commit, commitIndex)}
  98. onPullRequestClick={() => handlePullRequestClick(commit, commitIndex)}
  99. />
  100. ))}
  101. </StyledPanel>
  102. </div>
  103. );
  104. }
  105. export const StyledPanel = styled(Panel)`
  106. margin: 0;
  107. `;
  108. const ExpandButton = styled('button')`
  109. display: flex;
  110. align-items: center;
  111. gap: ${space(0.5)};
  112. `;