dashboards.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. import omit from 'lodash/omit';
  2. import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
  3. import type {Client} from 'sentry/api';
  4. import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
  5. import {t} from 'sentry/locale';
  6. import PageFiltersStore from 'sentry/stores/pageFiltersStore';
  7. import type {PageFilters} from 'sentry/types/core';
  8. import type {
  9. DashboardDetails,
  10. DashboardListItem,
  11. Widget,
  12. } from 'sentry/views/dashboards/types';
  13. import {flattenErrors} from 'sentry/views/dashboards/utils';
  14. export function fetchDashboards(api: Client, orgSlug: string) {
  15. const promise: Promise<DashboardListItem[]> = api.requestPromise(
  16. `/organizations/${orgSlug}/dashboards/`,
  17. {
  18. method: 'GET',
  19. query: {sort: 'myDashboardsAndRecentlyViewed'},
  20. }
  21. );
  22. promise.catch(response => {
  23. const errorResponse = response?.responseJSON ?? null;
  24. if (errorResponse) {
  25. const errors = flattenErrors(errorResponse, {});
  26. addErrorMessage(errors[Object.keys(errors)[0]!] as string);
  27. } else {
  28. addErrorMessage(t('Unable to fetch dashboards'));
  29. }
  30. });
  31. return promise;
  32. }
  33. export function createDashboard(
  34. api: Client,
  35. orgSlug: string,
  36. newDashboard: DashboardDetails,
  37. duplicate?: boolean
  38. ): Promise<DashboardDetails> {
  39. const {title, widgets, projects, environment, period, start, end, filters, utc} =
  40. newDashboard;
  41. const promise: Promise<DashboardDetails> = api.requestPromise(
  42. `/organizations/${orgSlug}/dashboards/`,
  43. {
  44. method: 'POST',
  45. data: {
  46. title,
  47. widgets: widgets.map(widget => omit(widget, ['tempId'])),
  48. duplicate,
  49. projects,
  50. environment,
  51. period,
  52. start,
  53. end,
  54. filters,
  55. utc,
  56. },
  57. query: {
  58. project: projects,
  59. environment,
  60. },
  61. }
  62. );
  63. promise.catch(response => {
  64. const errorResponse = response?.responseJSON ?? null;
  65. if (errorResponse) {
  66. const errors = flattenErrors(errorResponse, {});
  67. addErrorMessage(errors[Object.keys(errors)[0]!] as string);
  68. } else {
  69. addErrorMessage(t('Unable to create dashboard'));
  70. }
  71. });
  72. return promise;
  73. }
  74. export function updateDashboardVisit(
  75. api: Client,
  76. orgId: string,
  77. dashboardId: string | string[]
  78. ): Promise<void> {
  79. const promise = api.requestPromise(
  80. `/organizations/${orgId}/dashboards/${dashboardId}/visit/`,
  81. {
  82. method: 'POST',
  83. }
  84. );
  85. return promise;
  86. }
  87. export async function updateDashboardFavorite(
  88. api: Client,
  89. orgId: string,
  90. dashboardId: string | string[],
  91. isFavorited: boolean
  92. ): Promise<void> {
  93. try {
  94. await api.requestPromise(
  95. `/organizations/${orgId}/dashboards/${dashboardId}/favorite/`,
  96. {
  97. method: 'PUT',
  98. data: {
  99. isFavorited,
  100. },
  101. }
  102. );
  103. addSuccessMessage(isFavorited ? t('Added as favorite') : t('Removed as favorite'));
  104. } catch (response) {
  105. const errorResponse = response?.responseJSON ?? null;
  106. if (errorResponse) {
  107. const errors = flattenErrors(errorResponse, {});
  108. addErrorMessage(errors[Object.keys(errors)[0]!]! as string);
  109. } else if (isFavorited) {
  110. addErrorMessage(t('Unable to favorite dashboard'));
  111. } else {
  112. addErrorMessage(t('Unable to unfavorite dashboard'));
  113. }
  114. throw response;
  115. }
  116. }
  117. export function fetchDashboard(
  118. api: Client,
  119. orgId: string,
  120. dashboardId: string
  121. ): Promise<DashboardDetails> {
  122. const promise: Promise<DashboardDetails> = api.requestPromise(
  123. `/organizations/${orgId}/dashboards/${dashboardId}/`,
  124. {
  125. method: 'GET',
  126. }
  127. );
  128. promise.catch(response => {
  129. const errorResponse = response?.responseJSON ?? null;
  130. if (errorResponse) {
  131. const errors = flattenErrors(errorResponse, {});
  132. addErrorMessage(errors[Object.keys(errors)[0]!] as string);
  133. } else {
  134. addErrorMessage(t('Unable to load dashboard'));
  135. }
  136. });
  137. return promise;
  138. }
  139. export function updateDashboard(
  140. api: Client,
  141. orgId: string,
  142. dashboard: DashboardDetails
  143. ): Promise<DashboardDetails> {
  144. const {title, widgets, projects, environment, period, start, end, filters, utc} =
  145. dashboard;
  146. const data = {
  147. title,
  148. widgets: widgets.map(widget => omit(widget, ['tempId'])),
  149. projects,
  150. environment,
  151. period,
  152. start,
  153. end,
  154. filters,
  155. utc,
  156. };
  157. const promise: Promise<DashboardDetails> = api.requestPromise(
  158. `/organizations/${orgId}/dashboards/${dashboard.id}/`,
  159. {
  160. method: 'PUT',
  161. data,
  162. query: {
  163. project: projects,
  164. environment,
  165. },
  166. }
  167. );
  168. // We let the callers of `updateDashboard` handle adding a success message, so
  169. // that it can be more specific than just "Dashboard updated," but do the
  170. // error-handling here, since it doesn't depend on the caller's context
  171. promise.catch(response => {
  172. const errorResponse = response?.responseJSON ?? null;
  173. if (errorResponse) {
  174. const errors = flattenErrors(errorResponse, {});
  175. addErrorMessage(errors[Object.keys(errors)[0]!] as string);
  176. } else {
  177. addErrorMessage(t('Unable to update dashboard'));
  178. }
  179. });
  180. return promise;
  181. }
  182. export function deleteDashboard(
  183. api: Client,
  184. orgId: string,
  185. dashboardId: string
  186. ): Promise<undefined> {
  187. const promise: Promise<undefined> = api.requestPromise(
  188. `/organizations/${orgId}/dashboards/${dashboardId}/`,
  189. {
  190. method: 'DELETE',
  191. }
  192. );
  193. promise.catch(response => {
  194. const errorResponse = response?.responseJSON ?? null;
  195. if (errorResponse) {
  196. const errors = flattenErrors(errorResponse, {});
  197. addErrorMessage(errors[Object.keys(errors)[0]!] as string);
  198. } else {
  199. addErrorMessage(t('Unable to delete dashboard'));
  200. }
  201. });
  202. return promise;
  203. }
  204. export function validateWidgetRequest(
  205. orgId: string,
  206. widget: Widget,
  207. selection: PageFilters
  208. ) {
  209. return [
  210. `/organizations/${orgId}/dashboards/widgets/`,
  211. {
  212. method: 'POST',
  213. data: widget,
  214. query: {
  215. // TODO: This should be replaced in the future with projects
  216. // when we save Dashboard page filters. This is being sent to
  217. // bypass validation when creating or updating dashboards
  218. project: [ALL_ACCESS_PROJECTS],
  219. environment: selection.environments,
  220. },
  221. },
  222. ] as const;
  223. }
  224. export function updateDashboardPermissions(
  225. api: Client,
  226. orgId: string,
  227. dashboard: DashboardDetails | DashboardListItem
  228. ): Promise<DashboardDetails> {
  229. const {permissions} = dashboard;
  230. const data = {
  231. permissions,
  232. };
  233. const promise: Promise<DashboardDetails> = api.requestPromise(
  234. `/organizations/${orgId}/dashboards/${dashboard.id}/`,
  235. {
  236. method: 'PUT',
  237. data,
  238. }
  239. );
  240. promise.catch(response => {
  241. const errorResponse = response?.responseJSON ?? null;
  242. if (errorResponse) {
  243. const errors = flattenErrors(errorResponse, {});
  244. addErrorMessage(errors[Object.keys(errors)[0]!]! as string);
  245. } else {
  246. addErrorMessage(t('Unable to update dashboard permissions'));
  247. }
  248. });
  249. return promise;
  250. }
  251. export function validateWidget(
  252. api: Client,
  253. orgId: string,
  254. widget: Widget
  255. ): Promise<undefined> {
  256. const {selection} = PageFiltersStore.getState();
  257. const widgetQuery = validateWidgetRequest(orgId, widget, selection);
  258. const promise: Promise<undefined> = api.requestPromise(widgetQuery[0], widgetQuery[1]);
  259. return promise;
  260. }