utils.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. import {
  2. deleteHomepageQuery,
  3. updateHomepageQuery,
  4. } from 'sentry/actionCreators/discoverHomepageQueries';
  5. import {
  6. createSavedQuery,
  7. deleteSavedQuery,
  8. updateSavedQuery,
  9. } from 'sentry/actionCreators/discoverSavedQueries';
  10. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  11. import {Client} from 'sentry/api';
  12. import {t} from 'sentry/locale';
  13. import {NewQuery, Organization, SavedQuery} from 'sentry/types';
  14. import {trackAnalytics} from 'sentry/utils/analytics';
  15. import {SaveQueryEventParameters} from 'sentry/utils/analytics/discoverAnalyticsEvents';
  16. import EventView from 'sentry/utils/discover/eventView';
  17. import {DisplayModes} from 'sentry/utils/discover/types';
  18. import {DisplayType} from 'sentry/views/dashboards/types';
  19. export function handleCreateQuery(
  20. api: Client,
  21. organization: Organization,
  22. eventView: EventView,
  23. yAxis: string[],
  24. // True if this is a brand new query being saved
  25. // False if this is a modification from a saved query
  26. isNewQuery: boolean = true
  27. ): Promise<SavedQuery> {
  28. const payload = eventView.toNewQuery();
  29. payload.yAxis = yAxis;
  30. trackAnalytics(getAnalyticsCreateEventKeyName(isNewQuery, 'request'), {
  31. organization,
  32. ...extractAnalyticsQueryFields(payload),
  33. });
  34. const promise = createSavedQuery(api, organization.slug, payload);
  35. promise
  36. .then((savedQuery: SavedQuery) => {
  37. addSuccessMessage(t('Query saved'));
  38. trackAnalytics(getAnalyticsCreateEventKeyName(isNewQuery, 'success'), {
  39. organization,
  40. ...extractAnalyticsQueryFields(payload),
  41. });
  42. return savedQuery;
  43. })
  44. .catch((err: Error) => {
  45. addErrorMessage(t('Query not saved'));
  46. trackAnalytics(getAnalyticsCreateEventKeyName(isNewQuery, 'failed'), {
  47. organization,
  48. ...extractAnalyticsQueryFields(payload),
  49. error:
  50. (err && err.message) ||
  51. `Could not save a ${isNewQuery ? 'new' : 'existing'} query`,
  52. });
  53. });
  54. return promise;
  55. }
  56. export function handleUpdateQuery(
  57. api: Client,
  58. organization: Organization,
  59. eventView: EventView,
  60. yAxis: string[]
  61. ): Promise<SavedQuery> {
  62. const payload = eventView.toNewQuery();
  63. payload.yAxis = yAxis;
  64. if (!eventView.name) {
  65. addErrorMessage(t('Please name your query'));
  66. return Promise.reject();
  67. }
  68. trackAnalytics('discover_v2.update_query_request', {
  69. organization,
  70. ...extractAnalyticsQueryFields(payload),
  71. });
  72. const promise = updateSavedQuery(api, organization.slug, payload);
  73. promise
  74. .then((savedQuery: SavedQuery) => {
  75. addSuccessMessage(t('Query updated'));
  76. trackAnalytics('discover_v2.update_query_success', {
  77. organization,
  78. ...extractAnalyticsQueryFields(payload),
  79. });
  80. // NOTE: there is no need to convert _saved into an EventView and push it
  81. // to the browser history, since this.props.eventView already
  82. // derives from location.
  83. return savedQuery;
  84. })
  85. .catch((err: Error) => {
  86. addErrorMessage(t('Query not updated'));
  87. trackAnalytics('discover_v2.update_query_failed', {
  88. organization,
  89. ...extractAnalyticsQueryFields(payload),
  90. error: (err && err.message) || 'Failed to update a query',
  91. });
  92. });
  93. return promise;
  94. }
  95. /**
  96. * Essentially the same as handleUpdateQuery, but specifically for changing the
  97. * name of the query
  98. */
  99. export function handleUpdateQueryName(
  100. api: Client,
  101. organization: Organization,
  102. eventView: EventView
  103. ) {
  104. const payload = eventView.toNewQuery();
  105. trackAnalytics('discover_v2.update_query_name_request', {
  106. organization,
  107. ...extractAnalyticsQueryFields(payload),
  108. });
  109. const promise = updateSavedQuery(api, organization.slug, payload);
  110. promise
  111. .then(_saved => {
  112. addSuccessMessage(t('Query name saved'));
  113. trackAnalytics('discover_v2.update_query_name_successs', {
  114. organization,
  115. ...extractAnalyticsQueryFields(payload),
  116. });
  117. })
  118. .catch((err: Error) => {
  119. addErrorMessage(t('Query name not saved'));
  120. trackAnalytics('discover_v2.update_query_failed', {
  121. organization,
  122. ...extractAnalyticsQueryFields(payload),
  123. error: (err && err.message) || 'Failed to update a query name',
  124. });
  125. });
  126. return promise;
  127. }
  128. export function handleDeleteQuery(
  129. api: Client,
  130. organization: Organization,
  131. eventView: EventView
  132. ): Promise<void> {
  133. trackAnalytics('discover_v2.delete_query_request', {
  134. organization,
  135. ...extractAnalyticsQueryFields(eventView.toNewQuery()),
  136. });
  137. const promise = deleteSavedQuery(api, organization.slug, eventView.id!);
  138. promise
  139. .then(() => {
  140. addSuccessMessage(t('Query deleted'));
  141. trackAnalytics('discover_v2.delete_query_success', {
  142. organization,
  143. ...extractAnalyticsQueryFields(eventView.toNewQuery()),
  144. });
  145. })
  146. .catch((err: Error) => {
  147. addErrorMessage(t('Query not deleted'));
  148. trackAnalytics('discover_v2.delete_query_failed', {
  149. organization,
  150. ...extractAnalyticsQueryFields(eventView.toNewQuery()),
  151. error: (err && err.message) || 'Failed to delete query',
  152. });
  153. });
  154. return promise;
  155. }
  156. export function handleUpdateHomepageQuery(
  157. api: Client,
  158. organization: Organization,
  159. query: NewQuery
  160. ) {
  161. const promise = updateHomepageQuery(api, organization.slug, query);
  162. return promise
  163. .then(savedQuery => {
  164. addSuccessMessage(t('Saved as Discover default'));
  165. return savedQuery;
  166. })
  167. .catch(() => {
  168. addErrorMessage(t('Unable to set query as Discover default'));
  169. });
  170. }
  171. export function handleResetHomepageQuery(api: Client, organization: Organization) {
  172. const promise = deleteHomepageQuery(api, organization.slug);
  173. return promise
  174. .then(() => {
  175. addSuccessMessage(t('Successfully removed Discover default'));
  176. })
  177. .catch(() => {
  178. addErrorMessage(t('Unable to remove Discover default'));
  179. });
  180. }
  181. export function getAnalyticsCreateEventKeyName(
  182. // True if this is a brand new query being saved
  183. // False if this is a modification from a saved query
  184. isNewQuery: boolean,
  185. type: 'request' | 'success' | 'failed'
  186. ): keyof SaveQueryEventParameters {
  187. return (
  188. isNewQuery
  189. ? 'discover_v2.save_new_query_' + type
  190. : 'discover_v2.save_existing_query_' + type
  191. ) as keyof SaveQueryEventParameters;
  192. }
  193. /**
  194. * Takes in a DiscoverV2 NewQuery object and returns a Partial containing
  195. * the desired fields to populate into reload analytics
  196. */
  197. export function extractAnalyticsQueryFields(payload: NewQuery): Partial<NewQuery> {
  198. const {projects, fields, query} = payload;
  199. return {
  200. projects,
  201. fields,
  202. query,
  203. };
  204. }
  205. export function displayModeToDisplayType(displayMode: DisplayModes): DisplayType {
  206. switch (displayMode) {
  207. case DisplayModes.BAR:
  208. return DisplayType.BAR;
  209. case DisplayModes.TOP5:
  210. return DisplayType.TOP_N;
  211. default:
  212. return DisplayType.LINE;
  213. }
  214. }