group.tsx 14 KB

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