tagStore.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import {createStore} from 'reflux';
  2. import {Tag, TagCollection} from 'sentry/types';
  3. import {SEMVER_TAGS} from 'sentry/utils/discover/fields';
  4. import {FieldKey, ISSUE_FIELDS} from 'sentry/utils/fields';
  5. import {CommonStoreDefinition} from './types';
  6. // This list is only used on issues. Events/discover
  7. // have their own field list that exists elsewhere.
  8. // contexts.key and contexts.value omitted on purpose.
  9. const BUILTIN_TAGS = ISSUE_FIELDS.reduce<TagCollection>((acc, tag) => {
  10. acc[tag] = {key: tag, name: tag};
  11. return acc;
  12. }, {});
  13. interface TagStoreDefinition extends CommonStoreDefinition<TagCollection> {
  14. getIssueAttributes(): TagCollection;
  15. getIssueTags(): TagCollection;
  16. loadTagsSuccess(data: Tag[]): void;
  17. reset(): void;
  18. state: TagCollection;
  19. }
  20. const storeConfig: TagStoreDefinition = {
  21. state: {},
  22. init() {
  23. this.state = {};
  24. },
  25. /**
  26. * Gets only predefined issue attributes
  27. */
  28. getIssueAttributes() {
  29. // TODO(mitsuhiko): what do we do with translations here?
  30. const isSuggestions = [
  31. 'resolved',
  32. 'unresolved',
  33. 'ignored',
  34. 'assigned',
  35. 'for_review',
  36. 'unassigned',
  37. 'linked',
  38. 'unlinked',
  39. ];
  40. const sortedTagKeys = Object.keys(this.state).sort((a, b) => {
  41. return a.toLowerCase().localeCompare(b.toLowerCase());
  42. });
  43. const tagCollection = {
  44. [FieldKey.IS]: {
  45. key: FieldKey.IS,
  46. name: 'Status',
  47. values: isSuggestions,
  48. maxSuggestedValues: isSuggestions.length,
  49. predefined: true,
  50. },
  51. [FieldKey.HAS]: {
  52. key: FieldKey.HAS,
  53. name: 'Has Tag',
  54. values: sortedTagKeys,
  55. predefined: true,
  56. },
  57. [FieldKey.ASSIGNED]: {
  58. key: FieldKey.ASSIGNED,
  59. name: 'Assigned To',
  60. values: [],
  61. predefined: true,
  62. },
  63. [FieldKey.BOOKMARKS]: {
  64. key: FieldKey.BOOKMARKS,
  65. name: 'Bookmarked By',
  66. values: [],
  67. predefined: true,
  68. },
  69. [FieldKey.ISSUE_CATEGORY]: {
  70. key: FieldKey.ISSUE_CATEGORY,
  71. name: 'Issue Category',
  72. values: ['error', 'performance'],
  73. predefined: true,
  74. },
  75. [FieldKey.ISSUE_TYPE]: {
  76. key: FieldKey.ISSUE_TYPE,
  77. name: 'Issue Type',
  78. values: ['performance_n_plus_one_db_queries'],
  79. predefined: true,
  80. },
  81. [FieldKey.LAST_SEEN]: {
  82. key: FieldKey.LAST_SEEN,
  83. name: 'Last Seen',
  84. values: [],
  85. predefined: false,
  86. },
  87. [FieldKey.FIRST_SEEN]: {
  88. key: FieldKey.FIRST_SEEN,
  89. name: 'First Seen',
  90. values: [],
  91. predefined: false,
  92. },
  93. [FieldKey.FIRST_RELEASE]: {
  94. key: FieldKey.FIRST_RELEASE,
  95. name: 'First Release',
  96. values: ['latest'],
  97. predefined: true,
  98. },
  99. [FieldKey.EVENT_TIMESTAMP]: {
  100. key: FieldKey.EVENT_TIMESTAMP,
  101. name: 'Event Timestamp',
  102. values: [],
  103. predefined: true,
  104. },
  105. [FieldKey.TIMES_SEEN]: {
  106. key: FieldKey.TIMES_SEEN,
  107. name: 'Times Seen',
  108. isInput: true,
  109. // Below values are required or else SearchBar will attempt to get values
  110. // This is required or else SearchBar will attempt to get values
  111. values: [],
  112. predefined: true,
  113. },
  114. [FieldKey.ASSIGNED_OR_SUGGESTED]: {
  115. key: FieldKey.ASSIGNED_OR_SUGGESTED,
  116. name: 'Assigned or Suggested',
  117. isInput: true,
  118. values: [],
  119. predefined: true,
  120. },
  121. };
  122. // Ony include fields that that are part of the ISSUE_FIELDS. This is
  123. // because we may sometimes have fields that are turned off by removing
  124. // them from ISSUE_FIELDS
  125. const filteredCollection = Object.entries(tagCollection).filter(([key]) =>
  126. ISSUE_FIELDS.includes(key as FieldKey)
  127. );
  128. return Object.fromEntries(filteredCollection);
  129. },
  130. /**
  131. * Get all tags including builtin issue tags and issue attributes
  132. */
  133. getIssueTags() {
  134. return {
  135. ...BUILTIN_TAGS,
  136. ...SEMVER_TAGS,
  137. // State tags should overwrite built ins.
  138. ...this.state,
  139. // We want issue attributes to overwrite any built in and state tags
  140. ...this.getIssueAttributes(),
  141. };
  142. },
  143. getState() {
  144. return this.state;
  145. },
  146. reset() {
  147. this.state = {};
  148. this.trigger(this.state);
  149. },
  150. loadTagsSuccess(data) {
  151. const newTags = data.reduce<TagCollection>((acc, tag) => {
  152. acc[tag.key] = {
  153. values: [],
  154. ...tag,
  155. };
  156. return acc;
  157. }, {});
  158. this.state = {...this.state, ...newTags};
  159. this.trigger(this.state);
  160. },
  161. };
  162. const TagStore = createStore(storeConfig);
  163. export default TagStore;