dashboards.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import omit from 'lodash/omit';
  2. import {addErrorMessage} 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 function fetchDashboard(
  88. api: Client,
  89. orgId: string,
  90. dashboardId: string
  91. ): Promise<DashboardDetails> {
  92. const promise: Promise<DashboardDetails> = api.requestPromise(
  93. `/organizations/${orgId}/dashboards/${dashboardId}/`,
  94. {
  95. method: 'GET',
  96. }
  97. );
  98. promise.catch(response => {
  99. const errorResponse = response?.responseJSON ?? null;
  100. if (errorResponse) {
  101. const errors = flattenErrors(errorResponse, {});
  102. addErrorMessage(errors[Object.keys(errors)[0]] as string);
  103. } else {
  104. addErrorMessage(t('Unable to load dashboard'));
  105. }
  106. });
  107. return promise;
  108. }
  109. export function updateDashboard(
  110. api: Client,
  111. orgId: string,
  112. dashboard: DashboardDetails
  113. ): Promise<DashboardDetails> {
  114. const {title, widgets, projects, environment, period, start, end, filters, utc} =
  115. dashboard;
  116. const data = {
  117. title,
  118. widgets: widgets.map(widget => omit(widget, ['tempId'])),
  119. projects,
  120. environment,
  121. period,
  122. start,
  123. end,
  124. filters,
  125. utc,
  126. };
  127. const promise: Promise<DashboardDetails> = api.requestPromise(
  128. `/organizations/${orgId}/dashboards/${dashboard.id}/`,
  129. {
  130. method: 'PUT',
  131. data,
  132. query: {
  133. project: projects,
  134. environment,
  135. },
  136. }
  137. );
  138. // We let the callers of `updateDashboard` handle adding a success message, so
  139. // that it can be more specific than just "Dashboard updated," but do the
  140. // error-handling here, since it doesn't depend on the caller's context
  141. promise.catch(response => {
  142. const errorResponse = response?.responseJSON ?? null;
  143. if (errorResponse) {
  144. const errors = flattenErrors(errorResponse, {});
  145. addErrorMessage(errors[Object.keys(errors)[0]] as string);
  146. } else {
  147. addErrorMessage(t('Unable to update dashboard'));
  148. }
  149. });
  150. return promise;
  151. }
  152. export function deleteDashboard(
  153. api: Client,
  154. orgId: string,
  155. dashboardId: string
  156. ): Promise<undefined> {
  157. const promise: Promise<undefined> = api.requestPromise(
  158. `/organizations/${orgId}/dashboards/${dashboardId}/`,
  159. {
  160. method: 'DELETE',
  161. }
  162. );
  163. promise.catch(response => {
  164. const errorResponse = response?.responseJSON ?? null;
  165. if (errorResponse) {
  166. const errors = flattenErrors(errorResponse, {});
  167. addErrorMessage(errors[Object.keys(errors)[0]] as string);
  168. } else {
  169. addErrorMessage(t('Unable to delete dashboard'));
  170. }
  171. });
  172. return promise;
  173. }
  174. export function validateWidgetRequest(
  175. orgId: string,
  176. widget: Widget,
  177. selection: PageFilters
  178. ) {
  179. return [
  180. `/organizations/${orgId}/dashboards/widgets/`,
  181. {
  182. method: 'POST',
  183. data: widget,
  184. query: {
  185. // TODO: This should be replaced in the future with projects
  186. // when we save Dashboard page filters. This is being sent to
  187. // bypass validation when creating or updating dashboards
  188. project: [ALL_ACCESS_PROJECTS],
  189. environment: selection.environments,
  190. },
  191. },
  192. ] as const;
  193. }
  194. export function updateDashboardPermissions(
  195. api: Client,
  196. orgId: string,
  197. dashboard: DashboardDetails | DashboardListItem
  198. ): Promise<DashboardDetails> {
  199. const {permissions} = dashboard;
  200. const data = {
  201. permissions,
  202. };
  203. const promise: Promise<DashboardDetails> = api.requestPromise(
  204. `/organizations/${orgId}/dashboards/${dashboard.id}/`,
  205. {
  206. method: 'PUT',
  207. data,
  208. }
  209. );
  210. promise.catch(response => {
  211. const errorResponse = response?.responseJSON ?? null;
  212. if (errorResponse) {
  213. const errors = flattenErrors(errorResponse, {});
  214. addErrorMessage(errors[Object.keys(errors)[0]] as string);
  215. } else {
  216. addErrorMessage(t('Unable to update dashboard permissions'));
  217. }
  218. });
  219. return promise;
  220. }
  221. export function validateWidget(
  222. api: Client,
  223. orgId: string,
  224. widget: Widget
  225. ): Promise<undefined> {
  226. const {selection} = PageFiltersStore.getState();
  227. const widgetQuery = validateWidgetRequest(orgId, widget, selection);
  228. const promise: Promise<undefined> = api.requestPromise(widgetQuery[0], widgetQuery[1]);
  229. return promise;
  230. }