group.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. import type {PlatformKey} from 'sentry/data/platformCategories';
  2. import {FieldKind} from 'sentry/utils/fields';
  3. import type {Actor, TimeseriesValue} from './core';
  4. import type {Event, EventMetadata, EventOrGroupType, Level} from './event';
  5. import type {Commit, PullRequest, Repository} from './integrations';
  6. import type {Team} from './organization';
  7. import type {Project} from './project';
  8. import type {Release} from './release';
  9. import type {AvatarUser, User} from './user';
  10. export type EntryData = Record<string, any | Array<any>>;
  11. /**
  12. * Saved issues searches
  13. */
  14. export type RecentSearch = {
  15. dateCreated: string;
  16. id: string;
  17. lastSeen: string;
  18. organizationId: string;
  19. query: string;
  20. type: SavedSearchType;
  21. };
  22. // XXX: Deprecated Sentry 9 attributes are not included here.
  23. export type SavedSearch = {
  24. dateCreated: string;
  25. id: string;
  26. isGlobal: boolean;
  27. isPinned: boolean;
  28. name: string;
  29. query: string;
  30. sort: string;
  31. type: SavedSearchType;
  32. visibility: SavedSearchVisibility;
  33. };
  34. export enum SavedSearchVisibility {
  35. Organization = 'organization',
  36. Owner = 'owner',
  37. OwnerPinned = 'owner_pinned',
  38. }
  39. export enum SavedSearchType {
  40. ISSUE = 0,
  41. EVENT = 1,
  42. SESSION = 2,
  43. REPLAY = 3,
  44. }
  45. export enum IssueCategory {
  46. PERFORMANCE = 'performance',
  47. ERROR = 'error',
  48. }
  49. export enum IssueType {
  50. ERROR = 'error',
  51. PERFORMANCE_CONSECUTIVE_DB_QUERIES = 'performance_consecutive_db_queries',
  52. PERFORMANCE_FILE_IO_MAIN_THREAD = 'performance_file_io_main_thread',
  53. PERFORMANCE_N_PLUS_ONE_API_CALLS = 'performance_n_plus_one_api_calls',
  54. PERFORMANCE_N_PLUS_ONE_DB_QUERIES = 'performance_n_plus_one_db_queries',
  55. PERFORMANCE_SLOW_DB_QUERY = 'performance_slow_db_query',
  56. PERFORMANCE_RENDER_BLOCKING_ASSET = 'performance_render_blocking_asset_span',
  57. PERFORMANCE_UNCOMPRESSED_ASSET = 'performance_uncompressed_assets',
  58. }
  59. type CapabilityInfo = {
  60. enabled: boolean;
  61. disabledReason?: string;
  62. };
  63. /**
  64. * Defines what capabilities a category of issue has. Not all categories of
  65. * issues work the same.
  66. */
  67. export type IssueCategoryCapabilities = {
  68. /**
  69. * Is the Attachments tab shown for this issue
  70. */
  71. attachments: CapabilityInfo;
  72. /**
  73. * Are codeowner features shown for this issue
  74. */
  75. codeowners: CapabilityInfo;
  76. /**
  77. * Can the issue be deleted
  78. */
  79. delete: CapabilityInfo;
  80. /**
  81. * Can the issue be deleted and discarded
  82. */
  83. deleteAndDiscard: CapabilityInfo;
  84. /**
  85. * Is the Grouping tab shown for this issue
  86. */
  87. grouping: CapabilityInfo;
  88. /**
  89. * Can the issue be ignored (and the dropdown options)
  90. */
  91. ignore: CapabilityInfo;
  92. /**
  93. * Can the issue be merged
  94. */
  95. merge: CapabilityInfo;
  96. /**
  97. * Is the Merged Issues tab shown for this issue
  98. */
  99. mergedIssues: CapabilityInfo;
  100. /**
  101. * Is the Replays tab shown for this issue
  102. */
  103. replays: CapabilityInfo;
  104. /**
  105. * Can the issue be shared
  106. */
  107. share: CapabilityInfo;
  108. /**
  109. * Is the Similar Issues tab shown for this issue
  110. */
  111. similarIssues: CapabilityInfo;
  112. /**
  113. * Is the User Feedback tab enabled for this issue
  114. */
  115. userFeedback: CapabilityInfo;
  116. };
  117. // endpoint: /api/0/issues/:issueId/attachments/?limit=50
  118. export type IssueAttachment = {
  119. dateCreated: string;
  120. event_id: string;
  121. headers: object;
  122. id: string;
  123. mimetype: string;
  124. name: string;
  125. sha1: string;
  126. size: number;
  127. type: string;
  128. };
  129. // endpoint: /api/0/projects/:orgSlug/:projSlug/events/:eventId/attachments/
  130. export type EventAttachment = IssueAttachment;
  131. /**
  132. * Issue Tags
  133. */
  134. export type Tag = {
  135. key: string;
  136. name: string;
  137. isInput?: boolean;
  138. kind?: FieldKind;
  139. /**
  140. * How many values should be suggested in autocomplete.
  141. * Overrides SmartSearchBar's `maxSearchItems` prop.
  142. */
  143. maxSuggestedValues?: number;
  144. predefined?: boolean;
  145. totalValues?: number;
  146. values?: string[];
  147. };
  148. export type TagCollection = Record<string, Tag>;
  149. export type TagValue = {
  150. count: number;
  151. firstSeen: string;
  152. lastSeen: string;
  153. name: string;
  154. value: string;
  155. email?: string;
  156. identifier?: string;
  157. ipAddress?: string;
  158. key?: string;
  159. query?: string;
  160. username?: string;
  161. } & AvatarUser;
  162. type Topvalue = {
  163. count: number;
  164. firstSeen: string;
  165. key: string;
  166. lastSeen: string;
  167. name: string;
  168. value: string;
  169. // Might not actually exist.
  170. query?: string;
  171. readable?: string;
  172. };
  173. export type TagWithTopValues = {
  174. key: string;
  175. name: string;
  176. topValues: Array<Topvalue>;
  177. totalValues: number;
  178. uniqueValues: number;
  179. canDelete?: boolean;
  180. };
  181. /**
  182. * Inbox, issue owners and Activity
  183. */
  184. export type InboxReasonDetails = {
  185. count?: number | null;
  186. until?: string | null;
  187. user_count?: number | null;
  188. user_window?: number | null;
  189. window?: number | null;
  190. };
  191. export type InboxDetails = {
  192. reason_details: InboxReasonDetails;
  193. date_added?: string;
  194. reason?: number;
  195. };
  196. export type SuggestedOwnerReason =
  197. | 'suspectCommit'
  198. | 'ownershipRule'
  199. | 'codeowners'
  200. | 'releaseCommit';
  201. // Received from the backend to denote suggested owners of an issue
  202. export type SuggestedOwner = {
  203. date_added: string;
  204. owner: string;
  205. type: SuggestedOwnerReason;
  206. };
  207. export type IssueOwnership = {
  208. autoAssignment:
  209. | 'Auto Assign to Suspect Commits'
  210. | 'Auto Assign to Issue Owner'
  211. | 'Turn off Auto-Assignment';
  212. codeownersAutoSync: boolean;
  213. dateCreated: string | null;
  214. fallthrough: boolean;
  215. isActive: boolean;
  216. lastUpdated: string | null;
  217. raw: string | null;
  218. };
  219. export enum GroupActivityType {
  220. NOTE = 'note',
  221. SET_RESOLVED = 'set_resolved',
  222. SET_RESOLVED_BY_AGE = 'set_resolved_by_age',
  223. SET_RESOLVED_IN_RELEASE = 'set_resolved_in_release',
  224. SET_RESOLVED_IN_COMMIT = 'set_resolved_in_commit',
  225. SET_RESOLVED_IN_PULL_REQUEST = 'set_resolved_in_pull_request',
  226. SET_UNRESOLVED = 'set_unresolved',
  227. SET_IGNORED = 'set_ignored',
  228. SET_PUBLIC = 'set_public',
  229. SET_PRIVATE = 'set_private',
  230. SET_REGRESSION = 'set_regression',
  231. CREATE_ISSUE = 'create_issue',
  232. UNMERGE_SOURCE = 'unmerge_source',
  233. UNMERGE_DESTINATION = 'unmerge_destination',
  234. FIRST_SEEN = 'first_seen',
  235. ASSIGNED = 'assigned',
  236. UNASSIGNED = 'unassigned',
  237. MERGE = 'merge',
  238. REPROCESS = 'reprocess',
  239. MARK_REVIEWED = 'mark_reviewed',
  240. }
  241. interface GroupActivityBase {
  242. dateCreated: string;
  243. id: string;
  244. project: Project;
  245. assignee?: string;
  246. issue?: Group;
  247. user?: null | User;
  248. }
  249. interface GroupActivityNote extends GroupActivityBase {
  250. data: {
  251. text: string;
  252. };
  253. type: GroupActivityType.NOTE;
  254. }
  255. interface GroupActivitySetResolved extends GroupActivityBase {
  256. data: Record<string, any>;
  257. type: GroupActivityType.SET_RESOLVED;
  258. }
  259. interface GroupActivitySetUnresolved extends GroupActivityBase {
  260. data: Record<string, any>;
  261. type: GroupActivityType.SET_UNRESOLVED;
  262. }
  263. interface GroupActivitySetPublic extends GroupActivityBase {
  264. data: Record<string, any>;
  265. type: GroupActivityType.SET_PUBLIC;
  266. }
  267. interface GroupActivitySetPrivate extends GroupActivityBase {
  268. data: Record<string, any>;
  269. type: GroupActivityType.SET_PRIVATE;
  270. }
  271. interface GroupActivitySetByAge extends GroupActivityBase {
  272. data: Record<string, any>;
  273. type: GroupActivityType.SET_RESOLVED_BY_AGE;
  274. }
  275. interface GroupActivityUnassigned extends GroupActivityBase {
  276. data: Record<string, any>;
  277. type: GroupActivityType.UNASSIGNED;
  278. }
  279. interface GroupActivityFirstSeen extends GroupActivityBase {
  280. data: Record<string, any>;
  281. type: GroupActivityType.FIRST_SEEN;
  282. }
  283. interface GroupActivityMarkReviewed extends GroupActivityBase {
  284. data: Record<string, any>;
  285. type: GroupActivityType.MARK_REVIEWED;
  286. }
  287. interface GroupActivityRegression extends GroupActivityBase {
  288. data: {
  289. version?: string;
  290. };
  291. type: GroupActivityType.SET_REGRESSION;
  292. }
  293. export interface GroupActivitySetByResolvedInRelease extends GroupActivityBase {
  294. data: {
  295. current_release_version?: string;
  296. version?: string;
  297. };
  298. type: GroupActivityType.SET_RESOLVED_IN_RELEASE;
  299. }
  300. interface GroupActivitySetByResolvedInCommit extends GroupActivityBase {
  301. data: {
  302. commit: Commit;
  303. };
  304. type: GroupActivityType.SET_RESOLVED_IN_COMMIT;
  305. }
  306. interface GroupActivitySetByResolvedInPullRequest extends GroupActivityBase {
  307. data: {
  308. pullRequest: PullRequest;
  309. };
  310. type: GroupActivityType.SET_RESOLVED_IN_PULL_REQUEST;
  311. }
  312. export interface GroupActivitySetIgnored extends GroupActivityBase {
  313. data: {
  314. ignoreCount?: number;
  315. ignoreDuration?: number;
  316. ignoreUntil?: string;
  317. ignoreUserCount?: number;
  318. ignoreUserWindow?: number;
  319. ignoreWindow?: number;
  320. };
  321. type: GroupActivityType.SET_IGNORED;
  322. }
  323. export interface GroupActivityReprocess extends GroupActivityBase {
  324. data: {
  325. eventCount: number;
  326. newGroupId: number;
  327. oldGroupId: number;
  328. };
  329. type: GroupActivityType.REPROCESS;
  330. }
  331. interface GroupActivityUnmergeDestination extends GroupActivityBase {
  332. data: {
  333. fingerprints: Array<string>;
  334. source?: {
  335. id: string;
  336. shortId: string;
  337. };
  338. };
  339. type: GroupActivityType.UNMERGE_DESTINATION;
  340. }
  341. interface GroupActivityUnmergeSource extends GroupActivityBase {
  342. data: {
  343. fingerprints: Array<string>;
  344. destination?: {
  345. id: string;
  346. shortId: string;
  347. };
  348. };
  349. type: GroupActivityType.UNMERGE_SOURCE;
  350. }
  351. interface GroupActivityMerge extends GroupActivityBase {
  352. data: {
  353. issues: Array<any>;
  354. };
  355. type: GroupActivityType.MERGE;
  356. }
  357. export interface GroupActivityAssigned extends GroupActivityBase {
  358. data: {
  359. assignee: string;
  360. assigneeType: string;
  361. user: Team | User;
  362. assigneeEmail?: string;
  363. /**
  364. * If the user was assigned via an integration
  365. */
  366. integration?: 'projectOwnership' | 'codeowners' | 'slack' | 'msteams';
  367. /** Codeowner or Project owner rule as a string */
  368. rule?: string;
  369. };
  370. type: GroupActivityType.ASSIGNED;
  371. }
  372. export interface GroupActivityCreateIssue extends GroupActivityBase {
  373. data: {
  374. location: string;
  375. provider: string;
  376. title: string;
  377. };
  378. type: GroupActivityType.CREATE_ISSUE;
  379. }
  380. export type GroupActivity =
  381. | GroupActivityNote
  382. | GroupActivitySetResolved
  383. | GroupActivitySetUnresolved
  384. | GroupActivitySetIgnored
  385. | GroupActivitySetByAge
  386. | GroupActivitySetByResolvedInRelease
  387. | GroupActivitySetByResolvedInCommit
  388. | GroupActivitySetByResolvedInPullRequest
  389. | GroupActivityFirstSeen
  390. | GroupActivityMerge
  391. | GroupActivityReprocess
  392. | GroupActivityUnassigned
  393. | GroupActivityMarkReviewed
  394. | GroupActivityUnmergeDestination
  395. | GroupActivitySetPublic
  396. | GroupActivitySetPrivate
  397. | GroupActivityRegression
  398. | GroupActivityUnmergeSource
  399. | GroupActivityAssigned
  400. | GroupActivityCreateIssue;
  401. export type Activity = GroupActivity;
  402. interface GroupFiltered {
  403. count: string;
  404. firstSeen: string;
  405. lastSeen: string;
  406. stats: Record<string, TimeseriesValue[]>;
  407. userCount: number;
  408. }
  409. export interface GroupStats extends GroupFiltered {
  410. filtered: GroupFiltered | null;
  411. id: string;
  412. // for issue alert previews, the last time a group triggered a rule
  413. lastTriggered?: string;
  414. lifetime?: GroupFiltered;
  415. sessionCount?: string | null;
  416. }
  417. export interface BaseGroupStatusReprocessing {
  418. status: 'reprocessing';
  419. statusDetails: {
  420. info: {
  421. dateCreated: string;
  422. totalEvents: number;
  423. } | null;
  424. pendingEvents: number;
  425. };
  426. }
  427. /**
  428. * Issue Resolution
  429. */
  430. export enum ResolutionStatus {
  431. RESOLVED = 'resolved',
  432. UNRESOLVED = 'unresolved',
  433. IGNORED = 'ignored',
  434. }
  435. export type ResolutionStatusDetails = {
  436. actor?: AvatarUser;
  437. autoResolved?: boolean;
  438. ignoreCount?: number;
  439. // Sent in requests. ignoreUntil is used in responses.
  440. ignoreDuration?: number;
  441. ignoreUntil?: string;
  442. ignoreUserCount?: number;
  443. ignoreUserWindow?: number;
  444. ignoreWindow?: number;
  445. inCommit?: {
  446. commit?: string;
  447. dateCreated?: string;
  448. id?: string;
  449. repository?: string | Repository;
  450. };
  451. inNextRelease?: boolean;
  452. inRelease?: string;
  453. repository?: string;
  454. };
  455. export type GroupStatusResolution = {
  456. status: ResolutionStatus;
  457. statusDetails: ResolutionStatusDetails;
  458. };
  459. export type GroupRelease = {
  460. firstRelease: Release;
  461. lastRelease: Release;
  462. };
  463. // TODO(ts): incomplete
  464. export interface BaseGroup extends GroupRelease {
  465. activity: GroupActivity[];
  466. annotations: string[];
  467. assignedTo: Actor;
  468. culprit: string;
  469. firstSeen: string;
  470. hasSeen: boolean;
  471. id: string;
  472. isBookmarked: boolean;
  473. isPublic: boolean;
  474. isSubscribed: boolean;
  475. isUnhandled: boolean;
  476. issueCategory: IssueCategory;
  477. issueType: IssueType;
  478. lastSeen: string;
  479. latestEvent: Event;
  480. level: Level;
  481. logger: string;
  482. metadata: EventMetadata;
  483. numComments: number;
  484. participants: User[];
  485. permalink: string;
  486. platform: PlatformKey;
  487. pluginActions: any[]; // TODO(ts)
  488. pluginContexts: any[]; // TODO(ts)
  489. pluginIssues: any[]; // TODO(ts)
  490. project: Project;
  491. seenBy: User[];
  492. shareId: string;
  493. shortId: string;
  494. status: string;
  495. subscriptionDetails: {disabled?: boolean; reason?: string} | null;
  496. tags: Pick<Tag, 'key' | 'name' | 'totalValues'>[];
  497. title: string;
  498. type: EventOrGroupType;
  499. userReportCount: number;
  500. inbox?: InboxDetails | null | false;
  501. owners?: SuggestedOwner[] | null;
  502. }
  503. export interface GroupReprocessing
  504. // BaseGroupStatusReprocessing status field (enum) incorrectly extends the BaseGroup status field (string) so we omit it.
  505. // A proper fix for this would be to make the status field an enum or string and correctly extend it.
  506. extends Omit<BaseGroup, 'status'>,
  507. GroupStats,
  508. BaseGroupStatusReprocessing {}
  509. export interface GroupResolution
  510. // GroupStatusResolution status field (enum) incorrectly extends the BaseGroup status field (string) so we omit it.
  511. // A proper fix for this would be to make the status field an enum or string and correctly extend it.
  512. extends Omit<BaseGroup, 'status'>,
  513. GroupStats,
  514. GroupStatusResolution {}
  515. export type Group = GroupResolution | GroupReprocessing;
  516. export interface GroupCollapseRelease
  517. extends Omit<Group, keyof GroupRelease>,
  518. Partial<GroupRelease> {}
  519. export type GroupTombstone = {
  520. actor: AvatarUser;
  521. culprit: string;
  522. id: string;
  523. level: Level;
  524. metadata: EventMetadata;
  525. title: string;
  526. };
  527. export type ProcessingIssueItem = {
  528. checksum: string;
  529. data: {
  530. // TODO(ts) This type is likely incomplete, but this is what
  531. // project processing issues settings uses.
  532. _scope: string;
  533. image_arch: string;
  534. image_path: string;
  535. image_uuid: string;
  536. };
  537. id: string;
  538. lastSeen: string;
  539. numEvents: number;
  540. type: string;
  541. };
  542. export type ProcessingIssue = {
  543. hasIssues: boolean;
  544. hasMoreResolveableIssues: boolean;
  545. issuesProcessing: number;
  546. lastSeen: string;
  547. numIssues: number;
  548. project: string;
  549. resolveableIssues: number;
  550. signedLink: string;
  551. issues?: ProcessingIssueItem[];
  552. };
  553. /**
  554. * Datascrubbing
  555. */
  556. export type Meta = {
  557. chunks: Array<ChunkType>;
  558. err: Array<MetaError>;
  559. len: number;
  560. rem: Array<MetaRemark>;
  561. };
  562. export type MetaError = string | [string, any];
  563. export type MetaRemark = Array<string | number>;
  564. export type ChunkType = {
  565. rule_id: string | number;
  566. text: string;
  567. type: string;
  568. remark?: string | number;
  569. };
  570. /**
  571. * User Feedback
  572. */
  573. export type UserReport = {
  574. comments: string;
  575. dateCreated: string;
  576. email: string;
  577. event: {eventID: string; id: string};
  578. eventID: string;
  579. id: string;
  580. issue: Group;
  581. name: string;
  582. user: User;
  583. };
  584. export type KeyValueListDataItem = {
  585. key: string;
  586. subject: string;
  587. actionButton?: React.ReactNode;
  588. isContextData?: boolean;
  589. isMultiValue?: boolean;
  590. meta?: Meta;
  591. subjectDataTestId?: string;
  592. subjectIcon?: React.ReactNode;
  593. value?: React.ReactNode;
  594. };
  595. export type KeyValueListData = KeyValueListDataItem[];
  596. // Response from ShortIdLookupEndpoint
  597. // /organizations/${orgId}/shortids/${query}/
  598. export type ShortIdResponse = {
  599. group: Group;
  600. groupId: string;
  601. organizationSlug: string;
  602. projectSlug: string;
  603. shortId: string;
  604. };
  605. /**
  606. * Note used in Group Activity and Alerts for users to comment
  607. */
  608. export type Note = {
  609. /**
  610. * Array of [id, display string] tuples used for @-mentions
  611. */
  612. mentions: [string, string][];
  613. /**
  614. * Note contents (markdown allowed)
  615. */
  616. text: string;
  617. };