plugins.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import {
  2. addErrorMessage,
  3. addLoadingMessage,
  4. addSuccessMessage,
  5. } from 'sentry/actionCreators/indicator';
  6. import {Client, RequestOptions} from 'sentry/api';
  7. import {t} from 'sentry/locale';
  8. import PluginsStore from 'sentry/stores/pluginsStore';
  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. PluginsStore.onUpdate(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. PluginsStore.onUpdateSuccess(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. PluginsStore.onUpdateError(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. PluginsStore.onFetchAll(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. PluginsStore.onFetchAllSuccess(data, {
  80. pageLinks: resp?.getResponseHeader('Link') ?? undefined,
  81. });
  82. return data;
  83. })
  84. .catch(err => {
  85. PluginsStore.onFetchAllError(err);
  86. throw new Error('Unable to fetch plugins');
  87. })
  88. .then(() => (activeFetch[path] = null));
  89. return request;
  90. }
  91. type EnableDisablePluginParams = Slugs;
  92. /**
  93. * Enables a plugin
  94. */
  95. export function enablePlugin(params: EnableDisablePluginParams) {
  96. addLoadingMessage(t('Enabling...'));
  97. return doUpdate({...params, update: {enabled: true}, method: 'POST'})
  98. .then(() => addSuccessMessage(t('Plugin was enabled')))
  99. .catch(() => addErrorMessage(t('Unable to enable plugin')));
  100. }
  101. /**
  102. * Disables a plugin
  103. */
  104. export function disablePlugin(params: EnableDisablePluginParams) {
  105. addLoadingMessage(t('Disabling...'));
  106. return doUpdate({...params, update: {enabled: false}, method: 'DELETE'})
  107. .then(() => addSuccessMessage(t('Plugin was disabled')))
  108. .catch(() => addErrorMessage(t('Unable to disable plugin')));
  109. }