group.tsx 15 KB

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