group.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  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. isOrgCustom: boolean;
  28. isPinned: boolean;
  29. name: string;
  30. query: string;
  31. sort: string;
  32. type: SavedSearchType;
  33. };
  34. export enum SavedSearchType {
  35. ISSUE = 0,
  36. EVENT = 1,
  37. SESSION = 2,
  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 = Omit<IssueAttachment, 'event_id'>;
  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: boolean;
  172. codeownersAutoSync: boolean;
  173. dateCreated: string;
  174. fallthrough: boolean;
  175. isActive: boolean;
  176. lastUpdated: string;
  177. raw: string;
  178. };
  179. export enum GroupActivityType {
  180. NOTE = 'note',
  181. SET_RESOLVED = 'set_resolved',
  182. SET_RESOLVED_BY_AGE = 'set_resolved_by_age',
  183. SET_RESOLVED_IN_RELEASE = 'set_resolved_in_release',
  184. SET_RESOLVED_IN_COMMIT = 'set_resolved_in_commit',
  185. SET_RESOLVED_IN_PULL_REQUEST = 'set_resolved_in_pull_request',
  186. SET_UNRESOLVED = 'set_unresolved',
  187. SET_IGNORED = 'set_ignored',
  188. SET_PUBLIC = 'set_public',
  189. SET_PRIVATE = 'set_private',
  190. SET_REGRESSION = 'set_regression',
  191. CREATE_ISSUE = 'create_issue',
  192. UNMERGE_SOURCE = 'unmerge_source',
  193. UNMERGE_DESTINATION = 'unmerge_destination',
  194. FIRST_SEEN = 'first_seen',
  195. ASSIGNED = 'assigned',
  196. UNASSIGNED = 'unassigned',
  197. MERGE = 'merge',
  198. REPROCESS = 'reprocess',
  199. MARK_REVIEWED = 'mark_reviewed',
  200. }
  201. interface GroupActivityBase {
  202. dateCreated: string;
  203. id: string;
  204. project: Project;
  205. assignee?: string;
  206. issue?: Group;
  207. user?: null | User;
  208. }
  209. interface GroupActivityNote extends GroupActivityBase {
  210. data: {
  211. text: string;
  212. };
  213. type: GroupActivityType.NOTE;
  214. }
  215. interface GroupActivitySetResolved extends GroupActivityBase {
  216. data: Record<string, any>;
  217. type: GroupActivityType.SET_RESOLVED;
  218. }
  219. interface GroupActivitySetUnresolved extends GroupActivityBase {
  220. data: Record<string, any>;
  221. type: GroupActivityType.SET_UNRESOLVED;
  222. }
  223. interface GroupActivitySetPublic extends GroupActivityBase {
  224. data: Record<string, any>;
  225. type: GroupActivityType.SET_PUBLIC;
  226. }
  227. interface GroupActivitySetPrivate extends GroupActivityBase {
  228. data: Record<string, any>;
  229. type: GroupActivityType.SET_PRIVATE;
  230. }
  231. interface GroupActivitySetByAge extends GroupActivityBase {
  232. data: Record<string, any>;
  233. type: GroupActivityType.SET_RESOLVED_BY_AGE;
  234. }
  235. interface GroupActivityUnassigned extends GroupActivityBase {
  236. data: Record<string, any>;
  237. type: GroupActivityType.UNASSIGNED;
  238. }
  239. interface GroupActivityFirstSeen extends GroupActivityBase {
  240. data: Record<string, any>;
  241. type: GroupActivityType.FIRST_SEEN;
  242. }
  243. interface GroupActivityMarkReviewed extends GroupActivityBase {
  244. data: Record<string, any>;
  245. type: GroupActivityType.MARK_REVIEWED;
  246. }
  247. interface GroupActivityRegression extends GroupActivityBase {
  248. data: {
  249. version?: string;
  250. };
  251. type: GroupActivityType.SET_REGRESSION;
  252. }
  253. export interface GroupActivitySetByResolvedInRelease extends GroupActivityBase {
  254. data: {
  255. current_release_version?: string;
  256. version?: string;
  257. };
  258. type: GroupActivityType.SET_RESOLVED_IN_RELEASE;
  259. }
  260. interface GroupActivitySetByResolvedInCommit extends GroupActivityBase {
  261. data: {
  262. commit: Commit;
  263. };
  264. type: GroupActivityType.SET_RESOLVED_IN_COMMIT;
  265. }
  266. interface GroupActivitySetByResolvedInPullRequest extends GroupActivityBase {
  267. data: {
  268. pullRequest: PullRequest;
  269. };
  270. type: GroupActivityType.SET_RESOLVED_IN_PULL_REQUEST;
  271. }
  272. export interface GroupActivitySetIgnored extends GroupActivityBase {
  273. data: {
  274. ignoreCount?: number;
  275. ignoreDuration?: number;
  276. ignoreUntil?: string;
  277. ignoreUserCount?: number;
  278. ignoreUserWindow?: number;
  279. ignoreWindow?: number;
  280. };
  281. type: GroupActivityType.SET_IGNORED;
  282. }
  283. export interface GroupActivityReprocess extends GroupActivityBase {
  284. data: {
  285. eventCount: number;
  286. newGroupId: number;
  287. oldGroupId: number;
  288. };
  289. type: GroupActivityType.REPROCESS;
  290. }
  291. interface GroupActivityUnmergeDestination extends GroupActivityBase {
  292. data: {
  293. fingerprints: Array<string>;
  294. source?: {
  295. id: string;
  296. shortId: string;
  297. };
  298. };
  299. type: GroupActivityType.UNMERGE_DESTINATION;
  300. }
  301. interface GroupActivityUnmergeSource extends GroupActivityBase {
  302. data: {
  303. fingerprints: Array<string>;
  304. destination?: {
  305. id: string;
  306. shortId: string;
  307. };
  308. };
  309. type: GroupActivityType.UNMERGE_SOURCE;
  310. }
  311. interface GroupActivityMerge extends GroupActivityBase {
  312. data: {
  313. issues: Array<any>;
  314. };
  315. type: GroupActivityType.MERGE;
  316. }
  317. export interface GroupActivityAssigned extends GroupActivityBase {
  318. data: {
  319. assignee: string;
  320. assigneeType: string;
  321. user: Team | User;
  322. assigneeEmail?: string;
  323. /**
  324. * If the user was assigned via an integration
  325. */
  326. integration?: 'projectOwnership' | 'codeowners' | 'slack' | 'msteams';
  327. /** Codeowner or Project owner rule as a string */
  328. rule?: string;
  329. };
  330. type: GroupActivityType.ASSIGNED;
  331. }
  332. export interface GroupActivityCreateIssue extends GroupActivityBase {
  333. data: {
  334. location: string;
  335. provider: string;
  336. title: string;
  337. };
  338. type: GroupActivityType.CREATE_ISSUE;
  339. }
  340. export type GroupActivity =
  341. | GroupActivityNote
  342. | GroupActivitySetResolved
  343. | GroupActivitySetUnresolved
  344. | GroupActivitySetIgnored
  345. | GroupActivitySetByAge
  346. | GroupActivitySetByResolvedInRelease
  347. | GroupActivitySetByResolvedInCommit
  348. | GroupActivitySetByResolvedInPullRequest
  349. | GroupActivityFirstSeen
  350. | GroupActivityMerge
  351. | GroupActivityReprocess
  352. | GroupActivityUnassigned
  353. | GroupActivityMarkReviewed
  354. | GroupActivityUnmergeDestination
  355. | GroupActivitySetPublic
  356. | GroupActivitySetPrivate
  357. | GroupActivityRegression
  358. | GroupActivityUnmergeSource
  359. | GroupActivityAssigned
  360. | GroupActivityCreateIssue;
  361. export type Activity = GroupActivity;
  362. interface GroupFiltered {
  363. count: string;
  364. firstSeen: string;
  365. lastSeen: string;
  366. stats: Record<string, TimeseriesValue[]>;
  367. userCount: number;
  368. }
  369. export interface GroupStats extends GroupFiltered {
  370. filtered: GroupFiltered | null;
  371. id: string;
  372. lifetime?: GroupFiltered;
  373. sessionCount?: string | null;
  374. }
  375. export interface BaseGroupStatusReprocessing {
  376. status: 'reprocessing';
  377. statusDetails: {
  378. info: {
  379. dateCreated: string;
  380. totalEvents: number;
  381. } | null;
  382. pendingEvents: number;
  383. };
  384. }
  385. /**
  386. * Issue Resolution
  387. */
  388. export enum ResolutionStatus {
  389. RESOLVED = 'resolved',
  390. UNRESOLVED = 'unresolved',
  391. IGNORED = 'ignored',
  392. }
  393. export type ResolutionStatusDetails = {
  394. actor?: AvatarUser;
  395. autoResolved?: boolean;
  396. ignoreCount?: number;
  397. // Sent in requests. ignoreUntil is used in responses.
  398. ignoreDuration?: number;
  399. ignoreUntil?: string;
  400. ignoreUserCount?: number;
  401. ignoreUserWindow?: number;
  402. ignoreWindow?: number;
  403. inCommit?: {
  404. commit?: string;
  405. dateCreated?: string;
  406. id?: string;
  407. repository?: string | Repository;
  408. };
  409. inNextRelease?: boolean;
  410. inRelease?: string;
  411. repository?: string;
  412. };
  413. export type GroupStatusResolution = {
  414. status: ResolutionStatus;
  415. statusDetails: ResolutionStatusDetails;
  416. };
  417. export type GroupRelease = {
  418. firstRelease: Release;
  419. lastRelease: Release;
  420. };
  421. // TODO(ts): incomplete
  422. export interface BaseGroup extends GroupRelease {
  423. activity: GroupActivity[];
  424. annotations: string[];
  425. assignedTo: Actor;
  426. culprit: string;
  427. firstSeen: string;
  428. hasSeen: boolean;
  429. id: string;
  430. isBookmarked: boolean;
  431. isPublic: boolean;
  432. isSubscribed: boolean;
  433. isUnhandled: boolean;
  434. issueCategory: IssueCategory;
  435. issueType: IssueType;
  436. lastSeen: string;
  437. latestEvent: Event;
  438. level: Level;
  439. logger: string;
  440. metadata: EventMetadata;
  441. numComments: number;
  442. participants: User[];
  443. permalink: string;
  444. platform: PlatformKey;
  445. pluginActions: any[]; // TODO(ts)
  446. pluginContexts: any[]; // TODO(ts)
  447. pluginIssues: any[]; // TODO(ts)
  448. project: Project;
  449. seenBy: User[];
  450. shareId: string;
  451. shortId: string;
  452. status: string;
  453. subscriptionDetails: {disabled?: boolean; reason?: string} | null;
  454. tags: Pick<Tag, 'key' | 'name' | 'totalValues'>[];
  455. title: string;
  456. type: EventOrGroupType;
  457. userReportCount: number;
  458. inbox?: InboxDetails | null | false;
  459. owners?: SuggestedOwner[] | null;
  460. }
  461. export interface GroupReprocessing
  462. // BaseGroupStatusReprocessing status field (enum) incorrectly extends the BaseGroup status field (string) so we omit it.
  463. // A proper fix for this would be to make the status field an enum or string and correctly extend it.
  464. extends Omit<BaseGroup, 'status'>,
  465. GroupStats,
  466. BaseGroupStatusReprocessing {}
  467. export interface GroupResolution
  468. // GroupStatusResolution status field (enum) incorrectly extends the BaseGroup status field (string) so we omit it.
  469. // A proper fix for this would be to make the status field an enum or string and correctly extend it.
  470. extends Omit<BaseGroup, 'status'>,
  471. GroupStats,
  472. GroupStatusResolution {}
  473. export type Group = GroupResolution | GroupReprocessing;
  474. export interface GroupCollapseRelease
  475. extends Omit<Group, keyof GroupRelease>,
  476. Partial<GroupRelease> {}
  477. export type GroupTombstone = {
  478. actor: AvatarUser;
  479. culprit: string;
  480. id: string;
  481. level: Level;
  482. metadata: EventMetadata;
  483. title: string;
  484. };
  485. export type ProcessingIssueItem = {
  486. checksum: string;
  487. data: {
  488. // TODO(ts) This type is likely incomplete, but this is what
  489. // project processing issues settings uses.
  490. _scope: string;
  491. image_arch: string;
  492. image_path: string;
  493. image_uuid: string;
  494. };
  495. id: string;
  496. lastSeen: string;
  497. numEvents: number;
  498. type: string;
  499. };
  500. export type ProcessingIssue = {
  501. hasIssues: boolean;
  502. hasMoreResolveableIssues: boolean;
  503. issuesProcessing: number;
  504. lastSeen: string;
  505. numIssues: number;
  506. project: string;
  507. resolveableIssues: number;
  508. signedLink: string;
  509. issues?: ProcessingIssueItem[];
  510. };
  511. /**
  512. * Datascrubbing
  513. */
  514. export type Meta = {
  515. chunks: Array<ChunkType>;
  516. err: Array<MetaError>;
  517. len: number;
  518. rem: Array<MetaRemark>;
  519. };
  520. export type MetaError = string | [string, any];
  521. export type MetaRemark = Array<string | number>;
  522. export type ChunkType = {
  523. rule_id: string | number;
  524. text: string;
  525. type: string;
  526. remark?: string | number;
  527. };
  528. /**
  529. * User Feedback
  530. */
  531. export type UserReport = {
  532. comments: string;
  533. dateCreated: string;
  534. email: string;
  535. event: {eventID: string; id: string};
  536. eventID: string;
  537. id: string;
  538. issue: Group;
  539. name: string;
  540. user: User;
  541. };
  542. export type KeyValueListData = {
  543. key: string;
  544. subject: string;
  545. actionButton?: React.ReactNode;
  546. isContextData?: boolean;
  547. meta?: Meta;
  548. subjectDataTestId?: string;
  549. subjectIcon?: React.ReactNode;
  550. value?: React.ReactNode;
  551. }[];
  552. // Response from ShortIdLookupEndpoint
  553. // /organizations/${orgId}/shortids/${query}/
  554. export type ShortIdResponse = {
  555. group: Group;
  556. groupId: string;
  557. organizationSlug: string;
  558. projectSlug: string;
  559. shortId: string;
  560. };
  561. /**
  562. * Note used in Group Activity and Alerts for users to comment
  563. */
  564. export type Note = {
  565. /**
  566. * Array of [id, display string] tuples used for @-mentions
  567. */
  568. mentions: [string, string][];
  569. /**
  570. * Note contents (markdown allowed)
  571. */
  572. text: string;
  573. };