plugins.tsx 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import {
  2. addErrorMessage,
  3. addLoadingMessage,
  4. addSuccessMessage,
  5. } from 'sentry/actionCreators/indicator';
  6. import PluginActions from 'sentry/actions/pluginActions';
  7. import {Client, RequestOptions} from 'sentry/api';
  8. import {t} from 'sentry/locale';
  9. import {Plugin} from 'sentry/types';
  10. const activeFetch = {};
  11. // PluginsStore always exists, so api client should be independent of component lifecycle
  12. const api = new Client();
  13. type Slugs = {
  14. /**
  15. * Organization slug
  16. */
  17. orgId: string;
  18. /**
  19. * Plugin slug
  20. */
  21. pluginId: string;
  22. /**
  23. * Project slug
  24. */
  25. projectId: string;
  26. };
  27. type DoUpdateParams = Slugs & {
  28. update: Partial<Plugin>;
  29. } & Partial<RequestOptions>;
  30. function doUpdate({orgId, projectId, pluginId, update, ...params}: DoUpdateParams) {
  31. PluginActions.update(pluginId, update);
  32. const request = api.requestPromise(
  33. `/projects/${orgId}/${projectId}/plugins/${pluginId}/`,
  34. {
  35. ...params,
  36. }
  37. );
  38. // This is intentionally not chained because we want the unhandled promise to be returned
  39. request
  40. .then(() => {
  41. PluginActions.updateSuccess(pluginId, update);
  42. })
  43. .catch(resp => {
  44. const err =
  45. resp && resp.responseJSON && typeof resp.responseJSON.detail === 'string'
  46. ? new Error(resp.responseJSON.detail)
  47. : new Error('Unable to update plugin');
  48. PluginActions.updateError(pluginId, update, err);
  49. });
  50. return request;
  51. }
  52. type FetchPluginsOptions = {
  53. /**
  54. * Reset will set loading state = true
  55. */
  56. resetLoading?: boolean;
  57. };
  58. /**
  59. * Fetches list of available plugins for a project
  60. */
  61. export function fetchPlugins(
  62. {orgId, projectId}: Pick<Slugs, 'orgId' | 'projectId'>,
  63. options?: FetchPluginsOptions
  64. ): Promise<Plugin[]> {
  65. const path = `/projects/${orgId}/${projectId}/plugins/`;
  66. // Make sure we throttle fetches
  67. if (activeFetch[path]) {
  68. return activeFetch[path];
  69. }
  70. PluginActions.fetchAll(options);
  71. const request = api.requestPromise(path, {
  72. method: 'GET',
  73. includeAllArgs: true,
  74. });
  75. activeFetch[path] = request;
  76. // This is intentionally not chained because we want the unhandled promise to be returned
  77. request
  78. .then(([data, _, resp]) => {
  79. PluginActions.fetchAllSuccess(data, {pageLinks: resp?.getResponseHeader('Link')});
  80. return data;
  81. })
  82. .catch(err => {
  83. PluginActions.fetchAllError(err);
  84. throw new Error('Unable to fetch plugins');
  85. })
  86. .then(() => (activeFetch[path] = null));
  87. return request;
  88. }
  89. type EnableDisablePluginParams = Slugs;
  90. /**
  91. * Enables a plugin
  92. */
  93. export function enablePlugin(params: EnableDisablePluginParams) {
  94. addLoadingMessage(t('Enabling...'));
  95. return doUpdate({...params, update: {enabled: true}, method: 'POST'})
  96. .then(() => addSuccessMessage(t('Plugin was enabled')))
  97. .catch(() => addErrorMessage(t('Unable to enable plugin')));
  98. }
  99. /**
  100. * Disables a plugin
  101. */
  102. export function disablePlugin(params: EnableDisablePluginParams) {
  103. addLoadingMessage(t('Disabling...'));
  104. return doUpdate({...params, update: {enabled: false}, method: 'DELETE'})
  105. .then(() => addSuccessMessage(t('Plugin was disabled')))
  106. .catch(() => addErrorMessage(t('Unable to disable plugin')));
  107. }